@nastechai/agent 0.16.0 → 0.17.0
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/eslint.config.js +23 -0
- package/index.html +24 -0
- package/package.json +54 -26
- package/package.json.bak +89 -0
- package/package.json.pub +88 -0
- package/src/App.tsx +1173 -0
- package/src/components/AuthWidget.tsx +150 -0
- package/src/components/AutoField.tsx +206 -0
- package/src/components/Backdrop.tsx +93 -0
- package/src/components/ChatSidebar.tsx +394 -0
- package/src/components/DeleteConfirmDialog.tsx +40 -0
- package/src/components/LanguageSwitcher.tsx +186 -0
- package/src/components/Markdown.tsx +383 -0
- package/src/components/ModelInfoCard.tsx +112 -0
- package/src/components/ModelPickerDialog.tsx +470 -0
- package/src/components/OAuthLoginModal.tsx +374 -0
- package/src/components/OAuthProvidersCard.tsx +287 -0
- package/src/components/PlatformsCard.tsx +97 -0
- package/src/components/ScheduleBuilder.tsx +273 -0
- package/src/components/SidebarFooter.tsx +42 -0
- package/src/components/SidebarStatusStrip.tsx +72 -0
- package/src/components/SlashPopover.tsx +171 -0
- package/src/components/ThemeSwitcher.tsx +243 -0
- package/src/components/ToolCall.tsx +228 -0
- package/src/components/ToolsetConfigDrawer.tsx +448 -0
- package/src/contexts/PageHeaderProvider.tsx +139 -0
- package/src/contexts/SystemActions.tsx +120 -0
- package/src/contexts/page-header-context.ts +12 -0
- package/src/contexts/system-actions-context.ts +18 -0
- package/src/contexts/usePageHeader.ts +10 -0
- package/src/contexts/useSystemActions.ts +15 -0
- package/src/hooks/useModalBehavior.ts +44 -0
- package/src/hooks/useSidebarStatus.ts +27 -0
- package/src/i18n/af.ts +702 -0
- package/src/i18n/context.tsx +123 -0
- package/src/i18n/de.ts +701 -0
- package/src/i18n/en.ts +708 -0
- package/src/i18n/es.ts +701 -0
- package/src/i18n/fr.ts +701 -0
- package/src/i18n/ga.ts +702 -0
- package/src/i18n/hu.ts +702 -0
- package/src/i18n/index.ts +2 -0
- package/src/i18n/it.ts +701 -0
- package/src/i18n/ja.ts +702 -0
- package/src/i18n/ko.ts +702 -0
- package/src/i18n/pt.ts +702 -0
- package/src/i18n/ru.ts +702 -0
- package/src/i18n/tr.ts +702 -0
- package/src/i18n/types.ts +710 -0
- package/src/i18n/uk.ts +702 -0
- package/src/i18n/zh-hant.ts +702 -0
- package/src/i18n/zh.ts +698 -0
- package/src/index.css +274 -0
- package/src/lib/api.ts +1585 -0
- package/src/lib/dashboard-flags.ts +15 -0
- package/src/lib/format.ts +9 -0
- package/src/lib/fuzzy.ts +192 -0
- package/src/lib/gatewayClient.ts +253 -0
- package/src/lib/nested.ts +23 -0
- package/src/lib/resolve-page-title.ts +41 -0
- package/src/lib/schedule.ts +382 -0
- package/src/lib/slashExec.ts +163 -0
- package/src/lib/utils.ts +35 -0
- package/src/main.tsx +25 -0
- package/src/pages/AnalyticsPage.tsx +601 -0
- package/src/pages/ChannelsPage.tsx +772 -0
- package/src/pages/ChatPage.tsx +889 -0
- package/src/pages/ConfigPage.tsx +660 -0
- package/src/pages/CronPage.tsx +524 -0
- package/src/pages/DocsPage.tsx +69 -0
- package/src/pages/EnvPage.tsx +918 -0
- package/src/pages/LogsPage.tsx +246 -0
- package/src/pages/McpPage.tsx +757 -0
- package/src/pages/ModelsPage.tsx +994 -0
- package/src/pages/PairingPage.tsx +276 -0
- package/src/pages/PluginsPage.tsx +580 -0
- package/src/pages/ProfilesPage.tsx +559 -0
- package/src/pages/SessionsPage.tsx +936 -0
- package/src/pages/SkillsPage.tsx +557 -0
- package/src/pages/SystemPage.tsx +1259 -0
- package/src/pages/WebhooksPage.tsx +483 -0
- package/src/plugins/PluginPage.tsx +64 -0
- package/src/plugins/index.ts +6 -0
- package/src/plugins/registry.ts +151 -0
- package/src/plugins/sdk.d.ts +160 -0
- package/src/plugins/slots.ts +199 -0
- package/src/plugins/types.ts +37 -0
- package/src/plugins/usePlugins.ts +133 -0
- package/src/themes/context.tsx +443 -0
- package/src/themes/fonts.ts +160 -0
- package/src/themes/index.ts +3 -0
- package/src/themes/presets.ts +477 -0
- package/src/themes/types.ts +187 -0
- package/tsconfig.app.json +34 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +26 -0
- package/vite.config.ts +124 -0
- package/vite.config.ts.timestamp-1780999102396-af6b77b30ebd8.mjs +105 -0
package/src/lib/api.ts
ADDED
|
@@ -0,0 +1,1585 @@
|
|
|
1
|
+
// The dashboard can be served either at the root of its host (e.g.
|
|
2
|
+
// https://kanban.tilos.com/) or under a URL prefix when reverse-proxied
|
|
3
|
+
// (e.g. https://mission-control.tilos.com/nastech/). The Python backend
|
|
4
|
+
// injects ``window.__NASTECH_BASE_PATH__`` into index.html based on the
|
|
5
|
+
// incoming ``X-Forwarded-Prefix`` header so the SPA can address its own
|
|
6
|
+
// ``/api/...`` and ``/dashboard-plugins/...`` URLs correctly without a
|
|
7
|
+
// rebuild. Empty string means "served at root".
|
|
8
|
+
function readBasePath(): string {
|
|
9
|
+
if (typeof window === "undefined") return "";
|
|
10
|
+
const raw = window.__NASTECH_BASE_PATH__ ?? "";
|
|
11
|
+
if (!raw) return "";
|
|
12
|
+
// Normalise: ensure leading slash, strip trailing slash.
|
|
13
|
+
const withLead = raw.startsWith("/") ? raw : `/${raw}`;
|
|
14
|
+
return withLead.replace(/\/+$/, "");
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export const NASTECH_BASE_PATH = readBasePath();
|
|
18
|
+
const BASE = NASTECH_BASE_PATH;
|
|
19
|
+
|
|
20
|
+
import type { DashboardTheme } from "@/themes/types";
|
|
21
|
+
|
|
22
|
+
// Ephemeral session token for protected endpoints.
|
|
23
|
+
// Injected into index.html by the server — never fetched via API.
|
|
24
|
+
declare global {
|
|
25
|
+
interface Window {
|
|
26
|
+
__NASTECH_SESSION_TOKEN__?: string;
|
|
27
|
+
__NASTECH_BASE_PATH__?: string;
|
|
28
|
+
/** Server-injected flag: ``true`` when the dashboard's OAuth gate is
|
|
29
|
+
* engaged (public bind, no ``--insecure``). Toggles the SPA's
|
|
30
|
+
* WS-upgrade path from legacy ``?token=`` to single-use ``?ticket=``
|
|
31
|
+
* fetched via :func:`getWsTicket`. */
|
|
32
|
+
__NASTECH_AUTH_REQUIRED__?: boolean;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
let _sessionToken: string | null = null;
|
|
36
|
+
const SESSION_HEADER = "X-NasTech-Session-Token";
|
|
37
|
+
|
|
38
|
+
function setSessionHeader(headers: Headers, token: string): void {
|
|
39
|
+
if (!headers.has(SESSION_HEADER)) {
|
|
40
|
+
headers.set(SESSION_HEADER, token);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function fetchJSON<T>(
|
|
45
|
+
url: string,
|
|
46
|
+
init?: RequestInit,
|
|
47
|
+
options?: FetchJSONOptions,
|
|
48
|
+
): Promise<T> {
|
|
49
|
+
// Inject the session token into all /api/ requests.
|
|
50
|
+
const headers = new Headers(init?.headers);
|
|
51
|
+
const token = window.__NASTECH_SESSION_TOKEN__;
|
|
52
|
+
if (token) {
|
|
53
|
+
setSessionHeader(headers, token);
|
|
54
|
+
}
|
|
55
|
+
const res = await fetch(`${BASE}${url}`, {
|
|
56
|
+
...init,
|
|
57
|
+
headers,
|
|
58
|
+
// ``credentials: 'include'`` so the cookie-auth path (gated mode) works
|
|
59
|
+
// for any fetch routed through here. Loopback mode is unaffected — the
|
|
60
|
+
// server doesn't read cookies and the legacy session-token header is
|
|
61
|
+
// already attached above.
|
|
62
|
+
credentials: init?.credentials ?? "include",
|
|
63
|
+
});
|
|
64
|
+
if (res.status === 401) {
|
|
65
|
+
// Phase 6: the gated middleware emits a structured envelope so the
|
|
66
|
+
// SPA can full-page-navigate to /login on session expiry. Parse it,
|
|
67
|
+
// and only redirect on the known error codes — domain-level 401s
|
|
68
|
+
// (e.g. "you don't have permission to read this monitor") bubble
|
|
69
|
+
// up as regular errors so callers can handle them.
|
|
70
|
+
let body: { error?: string; login_url?: string } = {};
|
|
71
|
+
try {
|
|
72
|
+
body = await res.clone().json();
|
|
73
|
+
} catch {
|
|
74
|
+
/* non-JSON 401 — let it fall through */
|
|
75
|
+
}
|
|
76
|
+
if (
|
|
77
|
+
(body.error === "unauthenticated" || body.error === "session_expired") &&
|
|
78
|
+
body.login_url
|
|
79
|
+
) {
|
|
80
|
+
// Preserve where the user was so /auth/callback can land them back
|
|
81
|
+
// after re-auth. The gate's login_url already carries a ``next=``
|
|
82
|
+
// built from the request path, but the SPA may be deep inside a
|
|
83
|
+
// SPA route the gate never saw — e.g. a hash route or a client-side
|
|
84
|
+
// /sessions/<id> deep link. Save the current location as a
|
|
85
|
+
// fallback the post-login handler can read.
|
|
86
|
+
try {
|
|
87
|
+
sessionStorage.setItem(
|
|
88
|
+
"nastech.lastLocation",
|
|
89
|
+
window.location.pathname + window.location.search,
|
|
90
|
+
);
|
|
91
|
+
} catch {
|
|
92
|
+
/* SSR / privacy mode — ignore */
|
|
93
|
+
}
|
|
94
|
+
window.location.assign(body.login_url);
|
|
95
|
+
// Never resolve — the page is about to unload.
|
|
96
|
+
return new Promise<T>(() => {});
|
|
97
|
+
}
|
|
98
|
+
// Loopback mode: ``_SESSION_TOKEN`` rotates on every server restart
|
|
99
|
+
// (``nastech update``, ``nastech gateway restart``, etc.). A tab kept
|
|
100
|
+
// open across the restart holds the OLD token in
|
|
101
|
+
// ``window.__NASTECH_SESSION_TOKEN__`` from the previous HTML render,
|
|
102
|
+
// so every fetch returns 401. The HTML is served ``Cache-Control:
|
|
103
|
+
// no-store`` so a reload picks up the freshly-injected token. Trigger
|
|
104
|
+
// that reload once on the first stale-token 401 — gated mode is
|
|
105
|
+
// handled above, so reaching here in gated mode means a real
|
|
106
|
+
// middleware failure that should not reload-loop.
|
|
107
|
+
if (!window.__NASTECH_AUTH_REQUIRED__ && !options?.allowUnauthorized) {
|
|
108
|
+
let alreadyReloaded = false;
|
|
109
|
+
try {
|
|
110
|
+
alreadyReloaded =
|
|
111
|
+
sessionStorage.getItem("nastech.tokenReloadAttempted") === "1";
|
|
112
|
+
} catch {
|
|
113
|
+
/* SSR / privacy mode — fall through to throw */
|
|
114
|
+
}
|
|
115
|
+
if (!alreadyReloaded) {
|
|
116
|
+
try {
|
|
117
|
+
sessionStorage.setItem("nastech.tokenReloadAttempted", "1");
|
|
118
|
+
} catch {
|
|
119
|
+
/* SSR / privacy mode — best effort */
|
|
120
|
+
}
|
|
121
|
+
window.location.reload();
|
|
122
|
+
return new Promise<T>(() => {});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (res.ok) {
|
|
127
|
+
// Clear the stale-token reload guard: a successful 2xx proves the
|
|
128
|
+
// current ``window.__NASTECH_SESSION_TOKEN__`` is valid, so the next
|
|
129
|
+
// 401 — if any — should be allowed to trigger its own reload cycle.
|
|
130
|
+
try {
|
|
131
|
+
sessionStorage.removeItem("nastech.tokenReloadAttempted");
|
|
132
|
+
} catch {
|
|
133
|
+
/* SSR / privacy mode — ignore */
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!res.ok) {
|
|
137
|
+
const text = await res.text().catch(() => res.statusText);
|
|
138
|
+
throw new Error(`${res.status}: ${text}`);
|
|
139
|
+
}
|
|
140
|
+
return res.json();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Encode a plugin registry key for URL paths (preserves `/` segment separators). */
|
|
144
|
+
function pluginPath(name: string): string {
|
|
145
|
+
return name.split("/").map(encodeURIComponent).join("/");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function getSessionToken(): Promise<string> {
|
|
149
|
+
if (_sessionToken) return _sessionToken;
|
|
150
|
+
const injected = window.__NASTECH_SESSION_TOKEN__;
|
|
151
|
+
if (injected) {
|
|
152
|
+
_sessionToken = injected;
|
|
153
|
+
return _sessionToken;
|
|
154
|
+
}
|
|
155
|
+
throw new Error("Session token not available — page must be served by the NasTech dashboard server");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Fetch a single-use ticket for a WebSocket upgrade in gated mode.
|
|
160
|
+
*
|
|
161
|
+
* The dashboard's gated-mode WS auth (``nastech_cli.web_server._ws_auth_ok``)
|
|
162
|
+
* rejects the legacy ``?token=<_SESSION_TOKEN>`` path and only accepts
|
|
163
|
+
* ``?ticket=<minted>`` consumed against the in-memory ticket store. Browsers
|
|
164
|
+
* can't set ``Authorization`` on a WS upgrade, so this round-trip via the
|
|
165
|
+
* authenticated REST endpoint is the bridge from cookie auth to WS auth.
|
|
166
|
+
*
|
|
167
|
+
* Tickets are single-use and TTL=30s — every WS connect attempt must
|
|
168
|
+
* fetch a fresh ticket.
|
|
169
|
+
*/
|
|
170
|
+
export async function getWsTicket(): Promise<{ ticket: string; ttl_seconds: number }> {
|
|
171
|
+
const res = await fetch(`${BASE}/api/auth/ws-ticket`, {
|
|
172
|
+
method: "POST",
|
|
173
|
+
credentials: "include",
|
|
174
|
+
});
|
|
175
|
+
if (!res.ok) {
|
|
176
|
+
throw new Error(`/api/auth/ws-ticket: HTTP ${res.status}`);
|
|
177
|
+
}
|
|
178
|
+
return res.json();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Resolve the auth query-param pair (``[name, value]``) for a WebSocket
|
|
183
|
+
* connect. In gated mode mints a fresh single-use ticket; in loopback
|
|
184
|
+
* mode returns the injected session token.
|
|
185
|
+
*/
|
|
186
|
+
export async function buildWsAuthParam(): Promise<[string, string]> {
|
|
187
|
+
if (window.__NASTECH_AUTH_REQUIRED__) {
|
|
188
|
+
const { ticket } = await getWsTicket();
|
|
189
|
+
return ["ticket", ticket];
|
|
190
|
+
}
|
|
191
|
+
const token = window.__NASTECH_SESSION_TOKEN__ ?? "";
|
|
192
|
+
return ["token", token];
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export const api = {
|
|
196
|
+
getStatus: () => fetchJSON<StatusResponse>("/api/status"),
|
|
197
|
+
/**
|
|
198
|
+
* Identity probe for the dashboard auth gate (Phase 7).
|
|
199
|
+
*
|
|
200
|
+
* Returns the verified Session as JSON when gated mode is active and a
|
|
201
|
+
* valid cookie is attached. Loopback mode is unaffected — the endpoint
|
|
202
|
+
* still exists but is never useful there (no Session, no cookie). The
|
|
203
|
+
* AuthWidget component swallows 401s from this call: if the gate isn't
|
|
204
|
+
* engaged, /api/auth/me returns 401 and the widget renders nothing.
|
|
205
|
+
*
|
|
206
|
+
* ``allowUnauthorized`` is load-bearing: in loopback mode this endpoint
|
|
207
|
+
* 401s by design, and fetchJSON's default loopback behaviour treats a
|
|
208
|
+
* 401 as a rotated session token and full-page-reloads to pick up a
|
|
209
|
+
* fresh one. Because every *other* dashboard request succeeds (and so
|
|
210
|
+
* clears the one-shot reload guard), that turns this expected 401 into
|
|
211
|
+
* an infinite reload loop. Opting out keeps the 401 a plain throw the
|
|
212
|
+
* widget can catch.
|
|
213
|
+
*/
|
|
214
|
+
getAuthMe: () =>
|
|
215
|
+
fetchJSON<AuthMeResponse>("/api/auth/me", undefined, {
|
|
216
|
+
allowUnauthorized: true,
|
|
217
|
+
}),
|
|
218
|
+
logout: () =>
|
|
219
|
+
fetch(`${BASE}/auth/logout`, {
|
|
220
|
+
method: "POST",
|
|
221
|
+
credentials: "include",
|
|
222
|
+
}).then((r) => {
|
|
223
|
+
// /auth/logout returns 302 → /login. Follow that with a full-page
|
|
224
|
+
// navigation rather than letting fetch() opaquely consume the
|
|
225
|
+
// redirect — the SPA needs to leave the protected area.
|
|
226
|
+
window.location.assign("/login");
|
|
227
|
+
return r;
|
|
228
|
+
}),
|
|
229
|
+
getSessions: (limit = 20, offset = 0) =>
|
|
230
|
+
fetchJSON<PaginatedSessions>(`/api/sessions?limit=${limit}&offset=${offset}`),
|
|
231
|
+
getSessionMessages: (id: string) =>
|
|
232
|
+
fetchJSON<SessionMessagesResponse>(`/api/sessions/${encodeURIComponent(id)}/messages`),
|
|
233
|
+
getSessionLatestDescendant: (id: string) =>
|
|
234
|
+
fetchJSON<SessionLatestDescendantResponse>(
|
|
235
|
+
`/api/sessions/${encodeURIComponent(id)}/latest-descendant`,
|
|
236
|
+
),
|
|
237
|
+
deleteSession: (id: string) =>
|
|
238
|
+
fetchJSON<{ ok: boolean }>(`/api/sessions/${encodeURIComponent(id)}`, {
|
|
239
|
+
method: "DELETE",
|
|
240
|
+
}),
|
|
241
|
+
getLogs: (params: { file?: string; lines?: number; level?: string; component?: string }) => {
|
|
242
|
+
const qs = new URLSearchParams();
|
|
243
|
+
if (params.file) qs.set("file", params.file);
|
|
244
|
+
if (params.lines) qs.set("lines", String(params.lines));
|
|
245
|
+
if (params.level && params.level !== "ALL") qs.set("level", params.level);
|
|
246
|
+
if (params.component && params.component !== "all") qs.set("component", params.component);
|
|
247
|
+
return fetchJSON<LogsResponse>(`/api/logs?${qs.toString()}`);
|
|
248
|
+
},
|
|
249
|
+
getAnalytics: (days: number) =>
|
|
250
|
+
fetchJSON<AnalyticsResponse>(`/api/analytics/usage?days=${days}`),
|
|
251
|
+
getModelsAnalytics: (days: number) =>
|
|
252
|
+
fetchJSON<ModelsAnalyticsResponse>(`/api/analytics/models?days=${days}`),
|
|
253
|
+
getConfig: () => fetchJSON<Record<string, unknown>>("/api/config"),
|
|
254
|
+
getDefaults: () => fetchJSON<Record<string, unknown>>("/api/config/defaults"),
|
|
255
|
+
getSchema: () => fetchJSON<{ fields: Record<string, unknown>; category_order: string[] }>("/api/config/schema"),
|
|
256
|
+
getModelInfo: () => fetchJSON<ModelInfoResponse>("/api/model/info"),
|
|
257
|
+
getModelOptions: () => fetchJSON<ModelOptionsResponse>("/api/model/options"),
|
|
258
|
+
getAuxiliaryModels: () => fetchJSON<AuxiliaryModelsResponse>("/api/model/auxiliary"),
|
|
259
|
+
setModelAssignment: (body: ModelAssignmentRequest) =>
|
|
260
|
+
fetchJSON<ModelAssignmentResponse>("/api/model/set", {
|
|
261
|
+
method: "POST",
|
|
262
|
+
headers: { "Content-Type": "application/json" },
|
|
263
|
+
body: JSON.stringify(body),
|
|
264
|
+
}),
|
|
265
|
+
saveConfig: (config: Record<string, unknown>) =>
|
|
266
|
+
fetchJSON<{ ok: boolean }>("/api/config", {
|
|
267
|
+
method: "PUT",
|
|
268
|
+
headers: { "Content-Type": "application/json" },
|
|
269
|
+
body: JSON.stringify({ config }),
|
|
270
|
+
}),
|
|
271
|
+
getConfigRaw: () => fetchJSON<{ yaml: string }>("/api/config/raw"),
|
|
272
|
+
saveConfigRaw: (yaml_text: string) =>
|
|
273
|
+
fetchJSON<{ ok: boolean }>("/api/config/raw", {
|
|
274
|
+
method: "PUT",
|
|
275
|
+
headers: { "Content-Type": "application/json" },
|
|
276
|
+
body: JSON.stringify({ yaml_text }),
|
|
277
|
+
}),
|
|
278
|
+
getEnvVars: () => fetchJSON<Record<string, EnvVarInfo>>("/api/env"),
|
|
279
|
+
setEnvVar: (key: string, value: string) =>
|
|
280
|
+
fetchJSON<{ ok: boolean }>("/api/env", {
|
|
281
|
+
method: "PUT",
|
|
282
|
+
headers: { "Content-Type": "application/json" },
|
|
283
|
+
body: JSON.stringify({ key, value }),
|
|
284
|
+
}),
|
|
285
|
+
deleteEnvVar: (key: string) =>
|
|
286
|
+
fetchJSON<{ ok: boolean }>("/api/env", {
|
|
287
|
+
method: "DELETE",
|
|
288
|
+
headers: { "Content-Type": "application/json" },
|
|
289
|
+
body: JSON.stringify({ key }),
|
|
290
|
+
}),
|
|
291
|
+
revealEnvVar: async (key: string) => {
|
|
292
|
+
const token = await getSessionToken();
|
|
293
|
+
return fetchJSON<{ key: string; value: string }>("/api/env/reveal", {
|
|
294
|
+
method: "POST",
|
|
295
|
+
headers: {
|
|
296
|
+
"Content-Type": "application/json",
|
|
297
|
+
[SESSION_HEADER]: token,
|
|
298
|
+
},
|
|
299
|
+
body: JSON.stringify({ key }),
|
|
300
|
+
});
|
|
301
|
+
},
|
|
302
|
+
|
|
303
|
+
// Cron jobs
|
|
304
|
+
getCronJobs: (profile = "all") =>
|
|
305
|
+
fetchJSON<CronJob[]>(`/api/cron/jobs?profile=${encodeURIComponent(profile)}`),
|
|
306
|
+
createCronJob: (job: { prompt: string; schedule: string; name?: string; deliver?: string }, profile = "default") =>
|
|
307
|
+
fetchJSON<CronJob>(`/api/cron/jobs?profile=${encodeURIComponent(profile)}`, {
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers: { "Content-Type": "application/json" },
|
|
310
|
+
body: JSON.stringify(job),
|
|
311
|
+
}),
|
|
312
|
+
pauseCronJob: (id: string, profile = "default") =>
|
|
313
|
+
fetchJSON<CronJob>(`/api/cron/jobs/${encodeURIComponent(id)}/pause?profile=${encodeURIComponent(profile)}`, { method: "POST" }),
|
|
314
|
+
resumeCronJob: (id: string, profile = "default") =>
|
|
315
|
+
fetchJSON<CronJob>(`/api/cron/jobs/${encodeURIComponent(id)}/resume?profile=${encodeURIComponent(profile)}`, { method: "POST" }),
|
|
316
|
+
triggerCronJob: (id: string, profile = "default") =>
|
|
317
|
+
fetchJSON<CronJob>(`/api/cron/jobs/${encodeURIComponent(id)}/trigger?profile=${encodeURIComponent(profile)}`, { method: "POST" }),
|
|
318
|
+
deleteCronJob: (id: string, profile = "default") =>
|
|
319
|
+
fetchJSON<{ ok: boolean }>(`/api/cron/jobs/${encodeURIComponent(id)}?profile=${encodeURIComponent(profile)}`, { method: "DELETE" }),
|
|
320
|
+
|
|
321
|
+
// Profiles (minimal)
|
|
322
|
+
getProfiles: () =>
|
|
323
|
+
fetchJSON<{ profiles: ProfileInfo[] }>("/api/profiles"),
|
|
324
|
+
createProfile: (body: { name: string; clone_from_default: boolean }) =>
|
|
325
|
+
fetchJSON<{ ok: boolean; name: string; path: string }>("/api/profiles", {
|
|
326
|
+
method: "POST",
|
|
327
|
+
headers: { "Content-Type": "application/json" },
|
|
328
|
+
body: JSON.stringify(body),
|
|
329
|
+
}),
|
|
330
|
+
renameProfile: (name: string, newName: string) =>
|
|
331
|
+
fetchJSON<{ ok: boolean; name: string; path: string }>(
|
|
332
|
+
`/api/profiles/${encodeURIComponent(name)}`,
|
|
333
|
+
{
|
|
334
|
+
method: "PATCH",
|
|
335
|
+
headers: { "Content-Type": "application/json" },
|
|
336
|
+
body: JSON.stringify({ new_name: newName }),
|
|
337
|
+
},
|
|
338
|
+
),
|
|
339
|
+
deleteProfile: (name: string) =>
|
|
340
|
+
fetchJSON<{ ok: boolean }>(
|
|
341
|
+
`/api/profiles/${encodeURIComponent(name)}`,
|
|
342
|
+
{ method: "DELETE" },
|
|
343
|
+
),
|
|
344
|
+
getProfileSetupCommand: (name: string) =>
|
|
345
|
+
fetchJSON<{ command: string }>(
|
|
346
|
+
`/api/profiles/${encodeURIComponent(name)}/setup-command`,
|
|
347
|
+
),
|
|
348
|
+
getProfileSoul: (name: string) =>
|
|
349
|
+
fetchJSON<{ content: string; exists: boolean }>(
|
|
350
|
+
`/api/profiles/${encodeURIComponent(name)}/soul`,
|
|
351
|
+
),
|
|
352
|
+
updateProfileSoul: (name: string, content: string) =>
|
|
353
|
+
fetchJSON<{ ok: boolean }>(
|
|
354
|
+
`/api/profiles/${encodeURIComponent(name)}/soul`,
|
|
355
|
+
{
|
|
356
|
+
method: "PUT",
|
|
357
|
+
headers: { "Content-Type": "application/json" },
|
|
358
|
+
body: JSON.stringify({ content }),
|
|
359
|
+
},
|
|
360
|
+
),
|
|
361
|
+
|
|
362
|
+
// Skills & Toolsets
|
|
363
|
+
getSkills: () => fetchJSON<SkillInfo[]>("/api/skills"),
|
|
364
|
+
toggleSkill: (name: string, enabled: boolean) =>
|
|
365
|
+
fetchJSON<{ ok: boolean }>("/api/skills/toggle", {
|
|
366
|
+
method: "PUT",
|
|
367
|
+
headers: { "Content-Type": "application/json" },
|
|
368
|
+
body: JSON.stringify({ name, enabled }),
|
|
369
|
+
}),
|
|
370
|
+
getToolsets: () => fetchJSON<ToolsetInfo[]>("/api/tools/toolsets"),
|
|
371
|
+
|
|
372
|
+
// Session search (FTS5)
|
|
373
|
+
searchSessions: (q: string) =>
|
|
374
|
+
fetchJSON<SessionSearchResponse>(`/api/sessions/search?q=${encodeURIComponent(q)}`),
|
|
375
|
+
|
|
376
|
+
// OAuth provider management
|
|
377
|
+
getOAuthProviders: () =>
|
|
378
|
+
fetchJSON<OAuthProvidersResponse>("/api/providers/oauth"),
|
|
379
|
+
disconnectOAuthProvider: async (providerId: string) => {
|
|
380
|
+
const token = await getSessionToken();
|
|
381
|
+
return fetchJSON<{ ok: boolean; provider: string }>(
|
|
382
|
+
`/api/providers/oauth/${encodeURIComponent(providerId)}`,
|
|
383
|
+
{
|
|
384
|
+
method: "DELETE",
|
|
385
|
+
headers: { [SESSION_HEADER]: token },
|
|
386
|
+
},
|
|
387
|
+
);
|
|
388
|
+
},
|
|
389
|
+
startOAuthLogin: async (providerId: string) => {
|
|
390
|
+
const token = await getSessionToken();
|
|
391
|
+
return fetchJSON<OAuthStartResponse>(
|
|
392
|
+
`/api/providers/oauth/${encodeURIComponent(providerId)}/start`,
|
|
393
|
+
{
|
|
394
|
+
method: "POST",
|
|
395
|
+
headers: {
|
|
396
|
+
"Content-Type": "application/json",
|
|
397
|
+
[SESSION_HEADER]: token,
|
|
398
|
+
},
|
|
399
|
+
body: "{}",
|
|
400
|
+
},
|
|
401
|
+
);
|
|
402
|
+
},
|
|
403
|
+
submitOAuthCode: async (providerId: string, sessionId: string, code: string) => {
|
|
404
|
+
const token = await getSessionToken();
|
|
405
|
+
return fetchJSON<OAuthSubmitResponse>(
|
|
406
|
+
`/api/providers/oauth/${encodeURIComponent(providerId)}/submit`,
|
|
407
|
+
{
|
|
408
|
+
method: "POST",
|
|
409
|
+
headers: {
|
|
410
|
+
"Content-Type": "application/json",
|
|
411
|
+
[SESSION_HEADER]: token,
|
|
412
|
+
},
|
|
413
|
+
body: JSON.stringify({ session_id: sessionId, code }),
|
|
414
|
+
},
|
|
415
|
+
);
|
|
416
|
+
},
|
|
417
|
+
pollOAuthSession: (providerId: string, sessionId: string) =>
|
|
418
|
+
fetchJSON<OAuthPollResponse>(
|
|
419
|
+
`/api/providers/oauth/${encodeURIComponent(providerId)}/poll/${encodeURIComponent(sessionId)}`,
|
|
420
|
+
),
|
|
421
|
+
cancelOAuthSession: async (sessionId: string) => {
|
|
422
|
+
const token = await getSessionToken();
|
|
423
|
+
return fetchJSON<{ ok: boolean }>(
|
|
424
|
+
`/api/providers/oauth/sessions/${encodeURIComponent(sessionId)}`,
|
|
425
|
+
{
|
|
426
|
+
method: "DELETE",
|
|
427
|
+
headers: { [SESSION_HEADER]: token },
|
|
428
|
+
},
|
|
429
|
+
);
|
|
430
|
+
},
|
|
431
|
+
|
|
432
|
+
// Gateway / update actions
|
|
433
|
+
restartGateway: () =>
|
|
434
|
+
fetchJSON<ActionResponse>("/api/gateway/restart", { method: "POST" }),
|
|
435
|
+
updateNasTech: () =>
|
|
436
|
+
fetchJSON<ActionResponse>("/api/nastech/update", { method: "POST" }),
|
|
437
|
+
getActionStatus: (name: string, lines = 200) =>
|
|
438
|
+
fetchJSON<ActionStatusResponse>(
|
|
439
|
+
`/api/actions/${encodeURIComponent(name)}/status?lines=${lines}`,
|
|
440
|
+
),
|
|
441
|
+
|
|
442
|
+
// Dashboard plugins
|
|
443
|
+
getPlugins: () =>
|
|
444
|
+
fetchJSON<PluginManifestResponse[]>("/api/dashboard/plugins"),
|
|
445
|
+
rescanPlugins: () =>
|
|
446
|
+
fetchJSON<{ ok: boolean; count: number }>("/api/dashboard/plugins/rescan"),
|
|
447
|
+
|
|
448
|
+
getPluginsHub: () => fetchJSON<PluginsHubResponse>("/api/dashboard/plugins/hub"),
|
|
449
|
+
|
|
450
|
+
installAgentPlugin: (body: AgentPluginInstallRequest) =>
|
|
451
|
+
fetchJSON<AgentPluginInstallResponse>("/api/dashboard/agent-plugins/install", {
|
|
452
|
+
method: "POST",
|
|
453
|
+
headers: { "Content-Type": "application/json" },
|
|
454
|
+
body: JSON.stringify({ ...body }),
|
|
455
|
+
}),
|
|
456
|
+
|
|
457
|
+
enableAgentPlugin: (name: string) =>
|
|
458
|
+
fetchJSON<{ ok: boolean; name: string; unchanged?: boolean }>(
|
|
459
|
+
`/api/dashboard/agent-plugins/${pluginPath(name)}/enable`,
|
|
460
|
+
{ method: "POST" },
|
|
461
|
+
),
|
|
462
|
+
|
|
463
|
+
disableAgentPlugin: (name: string) =>
|
|
464
|
+
fetchJSON<{ ok: boolean; name: string; unchanged?: boolean }>(
|
|
465
|
+
`/api/dashboard/agent-plugins/${pluginPath(name)}/disable`,
|
|
466
|
+
{ method: "POST" },
|
|
467
|
+
),
|
|
468
|
+
|
|
469
|
+
updateAgentPlugin: (name: string) =>
|
|
470
|
+
fetchJSON<AgentPluginUpdateResponse>(
|
|
471
|
+
`/api/dashboard/agent-plugins/${pluginPath(name)}/update`,
|
|
472
|
+
{ method: "POST" },
|
|
473
|
+
),
|
|
474
|
+
|
|
475
|
+
removeAgentPlugin: (name: string) =>
|
|
476
|
+
fetchJSON<{ ok: boolean; name: string }>(
|
|
477
|
+
`/api/dashboard/agent-plugins/${pluginPath(name)}`,
|
|
478
|
+
{ method: "DELETE" },
|
|
479
|
+
),
|
|
480
|
+
|
|
481
|
+
savePluginProviders: (body: PluginProvidersPutRequest) =>
|
|
482
|
+
fetchJSON<{ ok: boolean }>("/api/dashboard/plugin-providers", {
|
|
483
|
+
method: "PUT",
|
|
484
|
+
headers: { "Content-Type": "application/json" },
|
|
485
|
+
body: JSON.stringify(body),
|
|
486
|
+
}),
|
|
487
|
+
|
|
488
|
+
setPluginVisibility: (name: string, hidden: boolean) =>
|
|
489
|
+
fetchJSON<{ ok: boolean; name: string; hidden: boolean }>(
|
|
490
|
+
`/api/dashboard/plugins/${pluginPath(name)}/visibility`,
|
|
491
|
+
{
|
|
492
|
+
method: "POST",
|
|
493
|
+
headers: { "Content-Type": "application/json" },
|
|
494
|
+
body: JSON.stringify({ hidden }),
|
|
495
|
+
},
|
|
496
|
+
),
|
|
497
|
+
|
|
498
|
+
// Dashboard themes
|
|
499
|
+
getThemes: () =>
|
|
500
|
+
fetchJSON<DashboardThemesResponse>("/api/dashboard/themes"),
|
|
501
|
+
setTheme: (name: string) =>
|
|
502
|
+
fetchJSON<{ ok: boolean; theme: string }>("/api/dashboard/theme", {
|
|
503
|
+
method: "PUT",
|
|
504
|
+
headers: { "Content-Type": "application/json" },
|
|
505
|
+
body: JSON.stringify({ name }),
|
|
506
|
+
}),
|
|
507
|
+
|
|
508
|
+
// Toolset config
|
|
509
|
+
getToolsetConfig: (name: string) =>
|
|
510
|
+
fetchJSON<ToolsetConfig>(`/api/tools/toolsets/${encodeURIComponent(name)}/config`),
|
|
511
|
+
toggleToolset: (name: string, enabled: boolean) =>
|
|
512
|
+
fetchJSON<{ ok: boolean; name: string; enabled: boolean }>(
|
|
513
|
+
`/api/tools/toolsets/${encodeURIComponent(name)}`,
|
|
514
|
+
{
|
|
515
|
+
method: "PUT",
|
|
516
|
+
headers: { "Content-Type": "application/json" },
|
|
517
|
+
body: JSON.stringify({ enabled }),
|
|
518
|
+
},
|
|
519
|
+
),
|
|
520
|
+
selectToolsetProvider: (name: string, provider: string) =>
|
|
521
|
+
fetchJSON<{ ok: boolean; name: string; provider: string }>(
|
|
522
|
+
`/api/tools/toolsets/${encodeURIComponent(name)}/provider`,
|
|
523
|
+
{
|
|
524
|
+
method: "PUT",
|
|
525
|
+
headers: { "Content-Type": "application/json" },
|
|
526
|
+
body: JSON.stringify({ provider }),
|
|
527
|
+
},
|
|
528
|
+
),
|
|
529
|
+
saveToolsetEnv: (name: string, env: Record<string, string>) =>
|
|
530
|
+
fetchJSON<{ ok: boolean; name: string; saved: string[]; skipped: string[]; is_set: Record<string, boolean> }>(
|
|
531
|
+
`/api/tools/toolsets/${encodeURIComponent(name)}/env`,
|
|
532
|
+
{
|
|
533
|
+
method: "PUT",
|
|
534
|
+
headers: { "Content-Type": "application/json" },
|
|
535
|
+
body: JSON.stringify({ env }),
|
|
536
|
+
},
|
|
537
|
+
),
|
|
538
|
+
runToolsetPostSetup: (name: string, key: string) =>
|
|
539
|
+
fetchJSON<ActionResponse>(
|
|
540
|
+
`/api/tools/toolsets/${encodeURIComponent(name)}/post-setup`,
|
|
541
|
+
{
|
|
542
|
+
method: "POST",
|
|
543
|
+
headers: { "Content-Type": "application/json" },
|
|
544
|
+
body: JSON.stringify({ key }),
|
|
545
|
+
},
|
|
546
|
+
),
|
|
547
|
+
|
|
548
|
+
// Messaging platforms
|
|
549
|
+
getMessagingPlatforms: () =>
|
|
550
|
+
fetchJSON<{ platforms: MessagingPlatform[] }>("/api/messaging/platforms"),
|
|
551
|
+
updateMessagingPlatform: (platformId: string, body: MessagingPlatformUpdate) =>
|
|
552
|
+
fetchJSON<MessagingPlatform>(
|
|
553
|
+
`/api/messaging/platforms/${encodeURIComponent(platformId)}`,
|
|
554
|
+
{
|
|
555
|
+
method: "PUT",
|
|
556
|
+
headers: { "Content-Type": "application/json" },
|
|
557
|
+
body: JSON.stringify(body),
|
|
558
|
+
},
|
|
559
|
+
),
|
|
560
|
+
testMessagingPlatform: (platformId: string) =>
|
|
561
|
+
fetchJSON<{ ok: boolean; message?: string }>(
|
|
562
|
+
`/api/messaging/platforms/${encodeURIComponent(platformId)}/test`,
|
|
563
|
+
{ method: "POST" },
|
|
564
|
+
),
|
|
565
|
+
startTelegramOnboarding: (body: { bot_name: string }) =>
|
|
566
|
+
fetchJSON<TelegramOnboardingStartResponse>(
|
|
567
|
+
"/api/messaging/telegram/onboarding/start",
|
|
568
|
+
{
|
|
569
|
+
method: "POST",
|
|
570
|
+
headers: { "Content-Type": "application/json" },
|
|
571
|
+
body: JSON.stringify(body),
|
|
572
|
+
},
|
|
573
|
+
),
|
|
574
|
+
getTelegramOnboardingStatus: (pairingId: string) =>
|
|
575
|
+
fetchJSON<{ status: string; bot_username?: string; owner_user_id?: string; expires_at?: string }>(
|
|
576
|
+
`/api/messaging/telegram/onboarding/${encodeURIComponent(pairingId)}`,
|
|
577
|
+
),
|
|
578
|
+
cancelTelegramOnboarding: (pairingId: string) =>
|
|
579
|
+
fetchJSON<{ ok: boolean }>(
|
|
580
|
+
`/api/messaging/telegram/onboarding/${encodeURIComponent(pairingId)}`,
|
|
581
|
+
{ method: "DELETE" },
|
|
582
|
+
),
|
|
583
|
+
applyTelegramOnboarding: (pairingId: string, body: { allowed_user_ids: string[] }) =>
|
|
584
|
+
fetchJSON<{ ok: boolean; platform: string; bot_username?: string; needs_restart: boolean }>(
|
|
585
|
+
`/api/messaging/telegram/onboarding/${encodeURIComponent(pairingId)}/apply`,
|
|
586
|
+
{
|
|
587
|
+
method: "POST",
|
|
588
|
+
headers: { "Content-Type": "application/json" },
|
|
589
|
+
body: JSON.stringify(body),
|
|
590
|
+
},
|
|
591
|
+
),
|
|
592
|
+
|
|
593
|
+
// MCP servers
|
|
594
|
+
getMcpServers: () =>
|
|
595
|
+
fetchJSON<{ servers: McpServer[] }>("/api/mcp/servers"),
|
|
596
|
+
addMcpServer: (body: McpServerCreate) =>
|
|
597
|
+
fetchJSON<McpServer>("/api/mcp/servers", {
|
|
598
|
+
method: "POST",
|
|
599
|
+
headers: { "Content-Type": "application/json" },
|
|
600
|
+
body: JSON.stringify(body),
|
|
601
|
+
}),
|
|
602
|
+
removeMcpServer: (name: string) =>
|
|
603
|
+
fetchJSON<{ ok: boolean }>(
|
|
604
|
+
`/api/mcp/servers/${encodeURIComponent(name)}`,
|
|
605
|
+
{ method: "DELETE" },
|
|
606
|
+
),
|
|
607
|
+
testMcpServer: (name: string) =>
|
|
608
|
+
fetchJSON<McpTestResult>(
|
|
609
|
+
`/api/mcp/servers/${encodeURIComponent(name)}/test`,
|
|
610
|
+
{ method: "POST" },
|
|
611
|
+
),
|
|
612
|
+
setMcpServerEnabled: (name: string, enabled: boolean) =>
|
|
613
|
+
fetchJSON<{ ok: boolean; name: string; enabled: boolean }>(
|
|
614
|
+
`/api/mcp/servers/${encodeURIComponent(name)}/enabled`,
|
|
615
|
+
{
|
|
616
|
+
method: "PUT",
|
|
617
|
+
headers: { "Content-Type": "application/json" },
|
|
618
|
+
body: JSON.stringify({ enabled }),
|
|
619
|
+
},
|
|
620
|
+
),
|
|
621
|
+
getMcpCatalog: () =>
|
|
622
|
+
fetchJSON<{ entries: McpCatalogEntry[]; diagnostics: McpCatalogDiagnostic[] }>("/api/mcp/catalog"),
|
|
623
|
+
installMcpCatalogEntry: (name: string, env: Record<string, string>, enable: boolean) =>
|
|
624
|
+
fetchJSON<{ ok: boolean; name: string; background: boolean; action?: string }>(
|
|
625
|
+
"/api/mcp/catalog/install",
|
|
626
|
+
{
|
|
627
|
+
method: "POST",
|
|
628
|
+
headers: { "Content-Type": "application/json" },
|
|
629
|
+
body: JSON.stringify({ name, env, enable }),
|
|
630
|
+
},
|
|
631
|
+
),
|
|
632
|
+
|
|
633
|
+
// Pairing
|
|
634
|
+
getPairing: () =>
|
|
635
|
+
fetchJSON<PairingResponse>("/api/pairing"),
|
|
636
|
+
approvePairing: (platform: string, code: string) =>
|
|
637
|
+
fetchJSON<{ ok: boolean; user?: PairingUser }>("/api/pairing/approve", {
|
|
638
|
+
method: "POST",
|
|
639
|
+
headers: { "Content-Type": "application/json" },
|
|
640
|
+
body: JSON.stringify({ platform, code }),
|
|
641
|
+
}),
|
|
642
|
+
revokePairing: (platform: string, user_id: string) =>
|
|
643
|
+
fetchJSON<{ ok: boolean }>("/api/pairing/revoke", {
|
|
644
|
+
method: "POST",
|
|
645
|
+
headers: { "Content-Type": "application/json" },
|
|
646
|
+
body: JSON.stringify({ platform, user_id }),
|
|
647
|
+
}),
|
|
648
|
+
clearPendingPairing: () =>
|
|
649
|
+
fetchJSON<{ ok: boolean; cleared: number }>("/api/pairing/clear-pending", {
|
|
650
|
+
method: "POST",
|
|
651
|
+
}),
|
|
652
|
+
|
|
653
|
+
// Webhooks
|
|
654
|
+
getWebhooks: () =>
|
|
655
|
+
fetchJSON<WebhooksResponse>("/api/webhooks"),
|
|
656
|
+
createWebhook: (body: Partial<WebhookRoute> & { name: string }) =>
|
|
657
|
+
fetchJSON<WebhookRoute & { secret?: string }>("/api/webhooks", {
|
|
658
|
+
method: "POST",
|
|
659
|
+
headers: { "Content-Type": "application/json" },
|
|
660
|
+
body: JSON.stringify(body),
|
|
661
|
+
}),
|
|
662
|
+
deleteWebhook: (name: string) =>
|
|
663
|
+
fetchJSON<{ ok: boolean }>(
|
|
664
|
+
`/api/webhooks/${encodeURIComponent(name)}`,
|
|
665
|
+
{ method: "DELETE" },
|
|
666
|
+
),
|
|
667
|
+
setWebhookEnabled: (name: string, enabled: boolean) =>
|
|
668
|
+
fetchJSON<{ ok: boolean; name: string; enabled: boolean }>(
|
|
669
|
+
`/api/webhooks/${encodeURIComponent(name)}/enabled`,
|
|
670
|
+
{
|
|
671
|
+
method: "PUT",
|
|
672
|
+
headers: { "Content-Type": "application/json" },
|
|
673
|
+
body: JSON.stringify({ enabled }),
|
|
674
|
+
},
|
|
675
|
+
),
|
|
676
|
+
|
|
677
|
+
// Memory
|
|
678
|
+
getMemoryStatus: () =>
|
|
679
|
+
fetchJSON<MemoryStatus>("/api/memory"),
|
|
680
|
+
getMemory: () =>
|
|
681
|
+
fetchJSON<MemoryStatus>("/api/memory"),
|
|
682
|
+
resetMemory: (target: string) =>
|
|
683
|
+
fetchJSON<{ ok: boolean; deleted: string[] }>("/api/memory/reset", {
|
|
684
|
+
method: "POST",
|
|
685
|
+
headers: { "Content-Type": "application/json" },
|
|
686
|
+
body: JSON.stringify({ target }),
|
|
687
|
+
}),
|
|
688
|
+
|
|
689
|
+
// System stats / checkpoints / hooks / curator / portal
|
|
690
|
+
getSystemStats: () =>
|
|
691
|
+
fetchJSON<SystemStats>("/api/system/stats"),
|
|
692
|
+
getCheckpoints: () =>
|
|
693
|
+
fetchJSON<CheckpointsResponse>("/api/ops/checkpoints"),
|
|
694
|
+
getHooks: () =>
|
|
695
|
+
fetchJSON<HooksResponse>("/api/ops/hooks"),
|
|
696
|
+
getCurator: () =>
|
|
697
|
+
fetchJSON<CuratorStatus>("/api/curator"),
|
|
698
|
+
getPortal: () =>
|
|
699
|
+
fetchJSON<PortalStatus>("/api/portal"),
|
|
700
|
+
startGateway: () =>
|
|
701
|
+
fetchJSON<ActionResponse>("/api/gateway/start", { method: "POST" }),
|
|
702
|
+
stopGateway: () =>
|
|
703
|
+
fetchJSON<ActionResponse>("/api/gateway/stop", { method: "POST" }),
|
|
704
|
+
|
|
705
|
+
// Curator
|
|
706
|
+
setCuratorPaused: (paused: boolean) =>
|
|
707
|
+
fetchJSON<{ ok: boolean; paused: boolean }>("/api/curator/paused", {
|
|
708
|
+
method: "PUT",
|
|
709
|
+
headers: { "Content-Type": "application/json" },
|
|
710
|
+
body: JSON.stringify({ paused }),
|
|
711
|
+
}),
|
|
712
|
+
runCurator: () =>
|
|
713
|
+
fetchJSON<ActionResponse>("/api/curator/run", { method: "POST" }),
|
|
714
|
+
|
|
715
|
+
// Ops / diagnostics
|
|
716
|
+
runDoctor: () =>
|
|
717
|
+
fetchJSON<ActionResponse>("/api/ops/doctor", { method: "POST" }),
|
|
718
|
+
runSecurityAudit: () =>
|
|
719
|
+
fetchJSON<ActionResponse>("/api/ops/security-audit", { method: "POST" }),
|
|
720
|
+
runBackup: (output?: string) =>
|
|
721
|
+
fetchJSON<ActionResponse>("/api/ops/backup", {
|
|
722
|
+
method: "POST",
|
|
723
|
+
headers: { "Content-Type": "application/json" },
|
|
724
|
+
body: JSON.stringify({ output: output ?? null }),
|
|
725
|
+
}),
|
|
726
|
+
runPromptSize: () =>
|
|
727
|
+
fetchJSON<ActionResponse>("/api/ops/prompt-size", { method: "POST" }),
|
|
728
|
+
runDump: () =>
|
|
729
|
+
fetchJSON<ActionResponse>("/api/ops/dump", { method: "POST" }),
|
|
730
|
+
runConfigMigrate: () =>
|
|
731
|
+
fetchJSON<ActionResponse>("/api/ops/config-migrate", { method: "POST" }),
|
|
732
|
+
runImport: (archive: string) =>
|
|
733
|
+
fetchJSON<ActionResponse>("/api/ops/import", {
|
|
734
|
+
method: "POST",
|
|
735
|
+
headers: { "Content-Type": "application/json" },
|
|
736
|
+
body: JSON.stringify({ archive }),
|
|
737
|
+
}),
|
|
738
|
+
pruneCheckpoints: () =>
|
|
739
|
+
fetchJSON<ActionResponse>("/api/ops/checkpoints/prune", { method: "POST" }),
|
|
740
|
+
runDebugShare: (body: { redact: boolean }) =>
|
|
741
|
+
fetchJSON<DebugShareResponse>(
|
|
742
|
+
"/api/ops/debug-share",
|
|
743
|
+
{
|
|
744
|
+
method: "POST",
|
|
745
|
+
headers: { "Content-Type": "application/json" },
|
|
746
|
+
body: JSON.stringify(body),
|
|
747
|
+
},
|
|
748
|
+
),
|
|
749
|
+
|
|
750
|
+
// Hooks
|
|
751
|
+
createHook: (body: { event: string; command: string; matcher?: string; timeout?: number; approve?: boolean }) =>
|
|
752
|
+
fetchJSON<{ ok: boolean; event: string; command: string; approved: boolean }>("/api/ops/hooks", {
|
|
753
|
+
method: "POST",
|
|
754
|
+
headers: { "Content-Type": "application/json" },
|
|
755
|
+
body: JSON.stringify(body),
|
|
756
|
+
}),
|
|
757
|
+
deleteHook: (event: string, command: string) =>
|
|
758
|
+
fetchJSON<{ ok: boolean; removed: boolean }>("/api/ops/hooks", {
|
|
759
|
+
method: "DELETE",
|
|
760
|
+
headers: { "Content-Type": "application/json" },
|
|
761
|
+
body: JSON.stringify({ event, command }),
|
|
762
|
+
}),
|
|
763
|
+
|
|
764
|
+
// Update check
|
|
765
|
+
checkNasTechUpdate: (force = false) =>
|
|
766
|
+
fetchJSON<UpdateCheckResponse>(`/api/nastech/update/check?force=${force}`),
|
|
767
|
+
|
|
768
|
+
// Credential pool
|
|
769
|
+
getCredentialPool: () =>
|
|
770
|
+
fetchJSON<{ providers: CredentialPoolProvider[] }>("/api/credentials/pool"),
|
|
771
|
+
addCredentialPoolEntry: (provider: string, api_key: string, label?: string) =>
|
|
772
|
+
fetchJSON<{ ok: boolean; provider: string; count: number }>("/api/credentials/pool", {
|
|
773
|
+
method: "POST",
|
|
774
|
+
headers: { "Content-Type": "application/json" },
|
|
775
|
+
body: JSON.stringify({ provider, api_key, label }),
|
|
776
|
+
}),
|
|
777
|
+
removeCredentialPoolEntry: (provider: string, index: number) =>
|
|
778
|
+
fetchJSON<{ ok: boolean; provider: string; count: number }>(
|
|
779
|
+
`/api/credentials/pool/${encodeURIComponent(provider)}/${index}`,
|
|
780
|
+
{ method: "DELETE" },
|
|
781
|
+
),
|
|
782
|
+
|
|
783
|
+
// Skills hub
|
|
784
|
+
updateSkillsFromHub: () =>
|
|
785
|
+
fetchJSON<ActionResponse>("/api/skills/hub/update", { method: "POST" }),
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
/** Identity payload returned by ``GET /api/auth/me`` (Phase 7).
|
|
789
|
+
*
|
|
790
|
+
* Returned by the dashboard's gated middleware when a valid session cookie
|
|
791
|
+
* is attached. ``email`` and ``display_name`` are empty strings under the
|
|
792
|
+
* NasTech Portal contract V1 (the access token has no email/name claims —
|
|
793
|
+
* see Contract Anchor C4 in the plan). The AuthWidget surfaces a
|
|
794
|
+
* truncated ``user_id`` instead.
|
|
795
|
+
*/
|
|
796
|
+
export interface AuthMeResponse {
|
|
797
|
+
user_id: string;
|
|
798
|
+
email: string;
|
|
799
|
+
display_name: string;
|
|
800
|
+
org_id: string;
|
|
801
|
+
provider: string;
|
|
802
|
+
expires_at: number;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
export interface ActionResponse {
|
|
806
|
+
name: string;
|
|
807
|
+
ok: boolean;
|
|
808
|
+
pid: number | null;
|
|
809
|
+
error?: string;
|
|
810
|
+
message?: string;
|
|
811
|
+
update_command?: string;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/** Per-call overrides for {@link fetchJSON}. */
|
|
815
|
+
interface FetchJSONOptions {
|
|
816
|
+
/** When true, a 401 response is surfaced as a normal thrown error rather
|
|
817
|
+
* than triggering the loopback stale-token page reload. Use for probes
|
|
818
|
+
* whose 401 is an expected signal (e.g. /api/auth/me in non-gated mode)
|
|
819
|
+
* rather than evidence of a rotated session token. */
|
|
820
|
+
allowUnauthorized?: boolean;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
export interface ActionStatusResponse {
|
|
824
|
+
exit_code: number | null;
|
|
825
|
+
lines: string[];
|
|
826
|
+
name: string;
|
|
827
|
+
pid: number | null;
|
|
828
|
+
running: boolean;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
export interface PlatformStatus {
|
|
832
|
+
error_code?: string;
|
|
833
|
+
error_message?: string;
|
|
834
|
+
state: string;
|
|
835
|
+
updated_at: string;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
export interface StatusResponse {
|
|
839
|
+
active_sessions: number;
|
|
840
|
+
/** Phase 7: ``true`` when the dashboard's OAuth gate is engaged
|
|
841
|
+
* (public bind, no ``--insecure``). Read alongside ``auth_providers``
|
|
842
|
+
* to render a "gated / loopback" badge. */
|
|
843
|
+
auth_required?: boolean;
|
|
844
|
+
/** Phase 7: registered ``DashboardAuthProvider`` names (e.g. ``["nastech"]``).
|
|
845
|
+
* Empty in loopback mode; empty + ``auth_required=true`` is a
|
|
846
|
+
* fail-closed state (the dashboard will refuse to bind). */
|
|
847
|
+
auth_providers?: string[];
|
|
848
|
+
config_path: string;
|
|
849
|
+
config_version: number;
|
|
850
|
+
env_path: string;
|
|
851
|
+
gateway_exit_reason: string | null;
|
|
852
|
+
gateway_health_url: string | null;
|
|
853
|
+
gateway_pid: number | null;
|
|
854
|
+
gateway_platforms: Record<string, PlatformStatus>;
|
|
855
|
+
gateway_running: boolean;
|
|
856
|
+
gateway_state: string | null;
|
|
857
|
+
gateway_updated_at: string | null;
|
|
858
|
+
nastech_home: string;
|
|
859
|
+
latest_config_version: number;
|
|
860
|
+
release_date: string;
|
|
861
|
+
version: string;
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
export interface SessionInfo {
|
|
865
|
+
id: string;
|
|
866
|
+
source: string | null;
|
|
867
|
+
model: string | null;
|
|
868
|
+
title: string | null;
|
|
869
|
+
started_at: number;
|
|
870
|
+
ended_at: number | null;
|
|
871
|
+
last_active: number;
|
|
872
|
+
is_active: boolean;
|
|
873
|
+
message_count: number;
|
|
874
|
+
tool_call_count: number;
|
|
875
|
+
input_tokens: number;
|
|
876
|
+
output_tokens: number;
|
|
877
|
+
preview: string | null;
|
|
878
|
+
parent_session_id?: string | null;
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
export interface SessionLatestDescendantResponse {
|
|
882
|
+
requested_session_id: string;
|
|
883
|
+
session_id: string;
|
|
884
|
+
path: string[];
|
|
885
|
+
changed: boolean;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
export interface PaginatedSessions {
|
|
889
|
+
sessions: SessionInfo[];
|
|
890
|
+
total: number;
|
|
891
|
+
limit: number;
|
|
892
|
+
offset: number;
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
export interface EnvVarInfo {
|
|
896
|
+
is_set: boolean;
|
|
897
|
+
redacted_value: string | null;
|
|
898
|
+
description: string;
|
|
899
|
+
url: string | null;
|
|
900
|
+
category: string;
|
|
901
|
+
is_password: boolean;
|
|
902
|
+
tools: string[];
|
|
903
|
+
advanced: boolean;
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
export interface SessionMessage {
|
|
907
|
+
role: "user" | "assistant" | "system" | "tool";
|
|
908
|
+
content: string | null;
|
|
909
|
+
tool_calls?: Array<{
|
|
910
|
+
id: string;
|
|
911
|
+
function: { name: string; arguments: string };
|
|
912
|
+
}>;
|
|
913
|
+
tool_name?: string;
|
|
914
|
+
tool_call_id?: string;
|
|
915
|
+
timestamp?: number;
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
export interface SessionMessagesResponse {
|
|
919
|
+
session_id: string;
|
|
920
|
+
messages: SessionMessage[];
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
export interface LogsResponse {
|
|
924
|
+
file: string;
|
|
925
|
+
lines: string[];
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
export interface AnalyticsDailyEntry {
|
|
929
|
+
day: string;
|
|
930
|
+
input_tokens: number;
|
|
931
|
+
output_tokens: number;
|
|
932
|
+
cache_read_tokens: number;
|
|
933
|
+
reasoning_tokens: number;
|
|
934
|
+
estimated_cost: number;
|
|
935
|
+
actual_cost: number;
|
|
936
|
+
sessions: number;
|
|
937
|
+
api_calls: number;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
export interface AnalyticsModelEntry {
|
|
941
|
+
model: string;
|
|
942
|
+
input_tokens: number;
|
|
943
|
+
output_tokens: number;
|
|
944
|
+
estimated_cost: number;
|
|
945
|
+
sessions: number;
|
|
946
|
+
api_calls: number;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
export interface AnalyticsSkillEntry {
|
|
950
|
+
skill: string;
|
|
951
|
+
view_count: number;
|
|
952
|
+
manage_count: number;
|
|
953
|
+
total_count: number;
|
|
954
|
+
percentage: number;
|
|
955
|
+
last_used_at: number | null;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
export interface AnalyticsSkillsSummary {
|
|
959
|
+
total_skill_loads: number;
|
|
960
|
+
total_skill_edits: number;
|
|
961
|
+
total_skill_actions: number;
|
|
962
|
+
distinct_skills_used: number;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
export interface AnalyticsResponse {
|
|
966
|
+
daily: AnalyticsDailyEntry[];
|
|
967
|
+
by_model: AnalyticsModelEntry[];
|
|
968
|
+
totals: {
|
|
969
|
+
total_input: number;
|
|
970
|
+
total_output: number;
|
|
971
|
+
total_cache_read: number;
|
|
972
|
+
total_reasoning: number;
|
|
973
|
+
total_estimated_cost: number;
|
|
974
|
+
total_actual_cost: number;
|
|
975
|
+
total_sessions: number;
|
|
976
|
+
total_api_calls: number;
|
|
977
|
+
};
|
|
978
|
+
skills: {
|
|
979
|
+
summary: AnalyticsSkillsSummary;
|
|
980
|
+
top_skills: AnalyticsSkillEntry[];
|
|
981
|
+
};
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
export interface ProfileInfo {
|
|
985
|
+
name: string;
|
|
986
|
+
path: string;
|
|
987
|
+
is_default: boolean;
|
|
988
|
+
model: string | null;
|
|
989
|
+
provider: string | null;
|
|
990
|
+
has_env: boolean;
|
|
991
|
+
skill_count: number;
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
export interface ModelsAnalyticsModelEntry {
|
|
995
|
+
model: string;
|
|
996
|
+
provider: string;
|
|
997
|
+
input_tokens: number;
|
|
998
|
+
output_tokens: number;
|
|
999
|
+
cache_read_tokens: number;
|
|
1000
|
+
reasoning_tokens: number;
|
|
1001
|
+
estimated_cost: number;
|
|
1002
|
+
actual_cost: number;
|
|
1003
|
+
sessions: number;
|
|
1004
|
+
api_calls: number;
|
|
1005
|
+
tool_calls: number;
|
|
1006
|
+
last_used_at: number;
|
|
1007
|
+
avg_tokens_per_session: number;
|
|
1008
|
+
capabilities: {
|
|
1009
|
+
supports_tools?: boolean;
|
|
1010
|
+
supports_vision?: boolean;
|
|
1011
|
+
supports_reasoning?: boolean;
|
|
1012
|
+
context_window?: number;
|
|
1013
|
+
max_output_tokens?: number;
|
|
1014
|
+
model_family?: string;
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
export interface ModelsAnalyticsResponse {
|
|
1019
|
+
models: ModelsAnalyticsModelEntry[];
|
|
1020
|
+
totals: {
|
|
1021
|
+
distinct_models: number;
|
|
1022
|
+
total_input: number;
|
|
1023
|
+
total_output: number;
|
|
1024
|
+
total_cache_read: number;
|
|
1025
|
+
total_reasoning: number;
|
|
1026
|
+
total_estimated_cost: number;
|
|
1027
|
+
total_actual_cost: number;
|
|
1028
|
+
total_sessions: number;
|
|
1029
|
+
total_api_calls: number;
|
|
1030
|
+
};
|
|
1031
|
+
period_days: number;
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
export interface CronJob {
|
|
1035
|
+
id: string;
|
|
1036
|
+
profile?: string | null;
|
|
1037
|
+
profile_name?: string | null;
|
|
1038
|
+
nastech_home?: string | null;
|
|
1039
|
+
is_default_profile?: boolean;
|
|
1040
|
+
name?: string | null;
|
|
1041
|
+
prompt?: string | null;
|
|
1042
|
+
script?: string | null;
|
|
1043
|
+
schedule?: { kind?: string; expr?: string; display?: string };
|
|
1044
|
+
schedule_display?: string | null;
|
|
1045
|
+
enabled: boolean;
|
|
1046
|
+
state?: string | null;
|
|
1047
|
+
deliver?: string | null;
|
|
1048
|
+
last_run_at?: string | null;
|
|
1049
|
+
next_run_at?: string | null;
|
|
1050
|
+
last_error?: string | null;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
export interface SkillInfo {
|
|
1054
|
+
name: string;
|
|
1055
|
+
description: string;
|
|
1056
|
+
category: string;
|
|
1057
|
+
enabled: boolean;
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
export interface ToolsetInfo {
|
|
1061
|
+
name: string;
|
|
1062
|
+
label: string;
|
|
1063
|
+
description: string;
|
|
1064
|
+
enabled: boolean;
|
|
1065
|
+
configured: boolean;
|
|
1066
|
+
tools: string[];
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
export interface SessionSearchResult {
|
|
1070
|
+
session_id: string;
|
|
1071
|
+
snippet: string;
|
|
1072
|
+
role: string | null;
|
|
1073
|
+
source: string | null;
|
|
1074
|
+
model: string | null;
|
|
1075
|
+
session_started: number | null;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
export interface SessionSearchResponse {
|
|
1079
|
+
results: SessionSearchResult[];
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// ── Model info types ──────────────────────────────────────────────────
|
|
1083
|
+
|
|
1084
|
+
export interface ModelInfoResponse {
|
|
1085
|
+
model: string;
|
|
1086
|
+
provider: string;
|
|
1087
|
+
auto_context_length: number;
|
|
1088
|
+
config_context_length: number;
|
|
1089
|
+
effective_context_length: number;
|
|
1090
|
+
capabilities: {
|
|
1091
|
+
supports_tools?: boolean;
|
|
1092
|
+
supports_vision?: boolean;
|
|
1093
|
+
supports_reasoning?: boolean;
|
|
1094
|
+
context_window?: number;
|
|
1095
|
+
max_output_tokens?: number;
|
|
1096
|
+
model_family?: string;
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// ── Model options / assignment types ──────────────────────────────────
|
|
1101
|
+
|
|
1102
|
+
export interface ModelOptionProvider {
|
|
1103
|
+
name: string;
|
|
1104
|
+
slug: string;
|
|
1105
|
+
models?: string[];
|
|
1106
|
+
total_models?: number;
|
|
1107
|
+
is_current?: boolean;
|
|
1108
|
+
is_user_defined?: boolean;
|
|
1109
|
+
source?: string;
|
|
1110
|
+
warning?: string;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
export interface ModelOptionsResponse {
|
|
1114
|
+
model?: string;
|
|
1115
|
+
provider?: string;
|
|
1116
|
+
providers?: ModelOptionProvider[];
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
export interface AuxiliaryTaskAssignment {
|
|
1120
|
+
task: string;
|
|
1121
|
+
provider: string;
|
|
1122
|
+
model: string;
|
|
1123
|
+
base_url: string;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
export interface AuxiliaryModelsResponse {
|
|
1127
|
+
tasks: AuxiliaryTaskAssignment[];
|
|
1128
|
+
main: { provider: string; model: string };
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
export interface ModelAssignmentRequest {
|
|
1132
|
+
scope: "main" | "auxiliary";
|
|
1133
|
+
provider: string;
|
|
1134
|
+
model: string;
|
|
1135
|
+
/** For auxiliary: task slot name, "" for all, "__reset__" to reset all. */
|
|
1136
|
+
task?: string;
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
export interface ModelAssignmentResponse {
|
|
1140
|
+
ok: boolean;
|
|
1141
|
+
scope?: string;
|
|
1142
|
+
provider?: string;
|
|
1143
|
+
model?: string;
|
|
1144
|
+
tasks?: string[];
|
|
1145
|
+
reset?: boolean;
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
// ── OAuth provider types ────────────────────────────────────────────────
|
|
1149
|
+
|
|
1150
|
+
export interface OAuthProviderStatus {
|
|
1151
|
+
logged_in: boolean;
|
|
1152
|
+
source?: string | null;
|
|
1153
|
+
source_label?: string | null;
|
|
1154
|
+
token_preview?: string | null;
|
|
1155
|
+
expires_at?: string | null;
|
|
1156
|
+
has_refresh_token?: boolean;
|
|
1157
|
+
last_refresh?: string | null;
|
|
1158
|
+
error?: string;
|
|
1159
|
+
}
|
|
1160
|
+
|
|
1161
|
+
export interface OAuthProvider {
|
|
1162
|
+
id: string;
|
|
1163
|
+
name: string;
|
|
1164
|
+
/** "pkce" (browser redirect + paste code), "device_code" (show code + URL),
|
|
1165
|
+
* or "external" (delegated to a separate CLI like Claude Code or Qwen). */
|
|
1166
|
+
flow: "pkce" | "device_code" | "external";
|
|
1167
|
+
cli_command: string;
|
|
1168
|
+
docs_url: string;
|
|
1169
|
+
status: OAuthProviderStatus;
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
export interface OAuthProvidersResponse {
|
|
1173
|
+
providers: OAuthProvider[];
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/** Discriminated union — the shape of /start depends on the flow. */
|
|
1177
|
+
export type OAuthStartResponse =
|
|
1178
|
+
| {
|
|
1179
|
+
session_id: string;
|
|
1180
|
+
flow: "pkce";
|
|
1181
|
+
auth_url: string;
|
|
1182
|
+
expires_in: number;
|
|
1183
|
+
}
|
|
1184
|
+
| {
|
|
1185
|
+
session_id: string;
|
|
1186
|
+
flow: "device_code";
|
|
1187
|
+
user_code: string;
|
|
1188
|
+
verification_url: string;
|
|
1189
|
+
expires_in: number;
|
|
1190
|
+
poll_interval: number;
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
export interface OAuthSubmitResponse {
|
|
1194
|
+
ok: boolean;
|
|
1195
|
+
status: "approved" | "error";
|
|
1196
|
+
message?: string;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
export interface OAuthPollResponse {
|
|
1200
|
+
session_id: string;
|
|
1201
|
+
status: "pending" | "approved" | "denied" | "expired" | "error";
|
|
1202
|
+
error_message?: string | null;
|
|
1203
|
+
expires_at?: number | null;
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
// ── Dashboard theme types ──────────────────────────────────────────────
|
|
1207
|
+
|
|
1208
|
+
export interface DashboardThemeSummary {
|
|
1209
|
+
description: string;
|
|
1210
|
+
label: string;
|
|
1211
|
+
name: string;
|
|
1212
|
+
/** Full theme definition for user themes; undefined for built-ins
|
|
1213
|
+
* (which the frontend already has locally). */
|
|
1214
|
+
definition?: DashboardTheme;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
export interface DashboardThemesResponse {
|
|
1218
|
+
active: string;
|
|
1219
|
+
themes: DashboardThemeSummary[];
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// ── Dashboard plugin types ─────────────────────────────────────────────
|
|
1223
|
+
|
|
1224
|
+
export interface PluginManifestResponse {
|
|
1225
|
+
name: string;
|
|
1226
|
+
label: string;
|
|
1227
|
+
description: string;
|
|
1228
|
+
icon: string;
|
|
1229
|
+
version: string;
|
|
1230
|
+
tab: {
|
|
1231
|
+
path: string;
|
|
1232
|
+
position?: string;
|
|
1233
|
+
override?: string;
|
|
1234
|
+
hidden?: boolean;
|
|
1235
|
+
};
|
|
1236
|
+
slots?: string[];
|
|
1237
|
+
entry: string;
|
|
1238
|
+
css?: string | null;
|
|
1239
|
+
has_api: boolean;
|
|
1240
|
+
source: string;
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
export interface HubAgentPluginRow {
|
|
1244
|
+
name: string;
|
|
1245
|
+
version: string;
|
|
1246
|
+
description: string;
|
|
1247
|
+
source: string;
|
|
1248
|
+
runtime_status: "disabled" | "enabled" | "inactive";
|
|
1249
|
+
has_dashboard_manifest: boolean;
|
|
1250
|
+
dashboard_manifest: PluginManifestResponse | null;
|
|
1251
|
+
path: string;
|
|
1252
|
+
can_remove: boolean;
|
|
1253
|
+
can_update_git: boolean;
|
|
1254
|
+
auth_required: boolean;
|
|
1255
|
+
auth_command: string;
|
|
1256
|
+
user_hidden: boolean;
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
export interface PluginsHubProviders {
|
|
1260
|
+
memory_provider: string;
|
|
1261
|
+
memory_options: Array<{ name: string; description: string }>;
|
|
1262
|
+
context_engine: string;
|
|
1263
|
+
context_options: Array<{ name: string; description: string }>;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
export interface PluginsHubResponse {
|
|
1267
|
+
plugins: HubAgentPluginRow[];
|
|
1268
|
+
orphan_dashboard_plugins: PluginManifestResponse[];
|
|
1269
|
+
providers: PluginsHubProviders;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
export interface AgentPluginInstallRequest {
|
|
1273
|
+
identifier: string;
|
|
1274
|
+
force?: boolean;
|
|
1275
|
+
enable?: boolean;
|
|
1276
|
+
}
|
|
1277
|
+
|
|
1278
|
+
export interface AgentPluginInstallResponse {
|
|
1279
|
+
ok: boolean;
|
|
1280
|
+
plugin_name?: string;
|
|
1281
|
+
warnings?: string[];
|
|
1282
|
+
missing_env?: string[];
|
|
1283
|
+
after_install_path?: string | null;
|
|
1284
|
+
enabled?: boolean;
|
|
1285
|
+
error?: string;
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
export interface AgentPluginUpdateResponse {
|
|
1289
|
+
ok: boolean;
|
|
1290
|
+
name?: string;
|
|
1291
|
+
output?: string;
|
|
1292
|
+
unchanged?: boolean;
|
|
1293
|
+
error?: string;
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
export interface PluginProvidersPutRequest {
|
|
1297
|
+
memory_provider?: string;
|
|
1298
|
+
context_engine?: string;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// ── Toolset config types ───────────────────────────────────────────────────
|
|
1302
|
+
|
|
1303
|
+
export interface ToolsetEnvVar {
|
|
1304
|
+
key: string;
|
|
1305
|
+
prompt: string;
|
|
1306
|
+
url?: string | null;
|
|
1307
|
+
default?: string | null;
|
|
1308
|
+
is_set: boolean;
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
export interface ToolsetProvider {
|
|
1312
|
+
name: string;
|
|
1313
|
+
label: string;
|
|
1314
|
+
description?: string;
|
|
1315
|
+
env_vars: ToolsetEnvVar[];
|
|
1316
|
+
post_setup?: string | null;
|
|
1317
|
+
requires_nastech_auth?: boolean;
|
|
1318
|
+
is_active: boolean;
|
|
1319
|
+
badge?: string;
|
|
1320
|
+
tag?: string;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
export interface ToolsetConfig {
|
|
1324
|
+
name: string;
|
|
1325
|
+
has_category: boolean;
|
|
1326
|
+
providers: ToolsetProvider[];
|
|
1327
|
+
active_provider: string | null;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
// ── Messaging platform types ───────────────────────────────────────────────
|
|
1331
|
+
|
|
1332
|
+
export interface MessagingPlatformEnvVar {
|
|
1333
|
+
key: string;
|
|
1334
|
+
required: boolean;
|
|
1335
|
+
is_set: boolean;
|
|
1336
|
+
redacted_value: string | null;
|
|
1337
|
+
prompt?: string;
|
|
1338
|
+
url?: string | null;
|
|
1339
|
+
description?: string;
|
|
1340
|
+
is_password?: boolean;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
export interface MessagingPlatform {
|
|
1344
|
+
id: string;
|
|
1345
|
+
name: string;
|
|
1346
|
+
description: string;
|
|
1347
|
+
docs_url?: string;
|
|
1348
|
+
enabled: boolean;
|
|
1349
|
+
configured: boolean;
|
|
1350
|
+
gateway_running: boolean;
|
|
1351
|
+
state: string | null;
|
|
1352
|
+
error_code?: string | null;
|
|
1353
|
+
error_message?: string | null;
|
|
1354
|
+
updated_at?: string | null;
|
|
1355
|
+
home_channel?: Record<string, unknown> | null;
|
|
1356
|
+
env_vars: MessagingPlatformEnvVar[];
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
export interface MessagingPlatformUpdate {
|
|
1360
|
+
enabled?: boolean;
|
|
1361
|
+
env?: Record<string, string>;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
export interface TelegramOnboardingStartResponse {
|
|
1365
|
+
pairing_id: string;
|
|
1366
|
+
suggested_username: string;
|
|
1367
|
+
deep_link: string;
|
|
1368
|
+
qr_payload: string;
|
|
1369
|
+
expires_at: string;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// ── MCP server types ───────────────────────────────────────────────────────
|
|
1373
|
+
|
|
1374
|
+
export interface McpServer {
|
|
1375
|
+
name: string;
|
|
1376
|
+
transport: string;
|
|
1377
|
+
url?: string | null;
|
|
1378
|
+
command?: string | null;
|
|
1379
|
+
args?: string[];
|
|
1380
|
+
env?: Record<string, string>;
|
|
1381
|
+
auth?: string | null;
|
|
1382
|
+
enabled: boolean;
|
|
1383
|
+
tools?: string[] | null;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
export interface McpServerCreate {
|
|
1387
|
+
name: string;
|
|
1388
|
+
url?: string;
|
|
1389
|
+
command?: string;
|
|
1390
|
+
args?: string[];
|
|
1391
|
+
env?: Record<string, string>;
|
|
1392
|
+
auth?: string;
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
export interface McpCatalogEnvVar {
|
|
1396
|
+
name: string;
|
|
1397
|
+
prompt: string;
|
|
1398
|
+
required: boolean;
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
export interface McpCatalogEntry {
|
|
1402
|
+
name: string;
|
|
1403
|
+
description: string;
|
|
1404
|
+
source: string;
|
|
1405
|
+
transport: string;
|
|
1406
|
+
auth_type: string;
|
|
1407
|
+
required_env: McpCatalogEnvVar[];
|
|
1408
|
+
needs_install: boolean;
|
|
1409
|
+
installed: boolean;
|
|
1410
|
+
enabled: boolean;
|
|
1411
|
+
}
|
|
1412
|
+
|
|
1413
|
+
export interface McpCatalogDiagnostic {
|
|
1414
|
+
name: string;
|
|
1415
|
+
kind: string;
|
|
1416
|
+
message: string;
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
export interface McpTestResult {
|
|
1420
|
+
ok: boolean;
|
|
1421
|
+
error?: string;
|
|
1422
|
+
tools: { name: string; description: string }[];
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
// ── Pairing types ──────────────────────────────────────────────────────────
|
|
1426
|
+
|
|
1427
|
+
export interface PairingUser {
|
|
1428
|
+
platform: string;
|
|
1429
|
+
user_id: string;
|
|
1430
|
+
user_name?: string;
|
|
1431
|
+
code?: string;
|
|
1432
|
+
approved_at?: string;
|
|
1433
|
+
age_minutes?: number;
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
export interface PairingResponse {
|
|
1437
|
+
pending: PairingUser[];
|
|
1438
|
+
approved: PairingUser[];
|
|
1439
|
+
}
|
|
1440
|
+
|
|
1441
|
+
// ── Webhook types ──────────────────────────────────────────────────────────
|
|
1442
|
+
|
|
1443
|
+
export interface WebhookRoute {
|
|
1444
|
+
name: string;
|
|
1445
|
+
description: string;
|
|
1446
|
+
events: string[];
|
|
1447
|
+
deliver: string;
|
|
1448
|
+
deliver_only: boolean;
|
|
1449
|
+
prompt: string;
|
|
1450
|
+
skills: string[];
|
|
1451
|
+
created_at?: string;
|
|
1452
|
+
url: string;
|
|
1453
|
+
secret_set: boolean;
|
|
1454
|
+
enabled: boolean;
|
|
1455
|
+
secret?: string;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
export interface WebhooksResponse {
|
|
1459
|
+
enabled: boolean;
|
|
1460
|
+
base_url: string;
|
|
1461
|
+
subscriptions: WebhookRoute[];
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
// ── Memory types ───────────────────────────────────────────────────────────
|
|
1465
|
+
|
|
1466
|
+
export interface MemoryProviderInfo {
|
|
1467
|
+
name: string;
|
|
1468
|
+
description: string;
|
|
1469
|
+
configured: boolean;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
export interface MemoryStatus {
|
|
1473
|
+
active: string;
|
|
1474
|
+
providers: MemoryProviderInfo[];
|
|
1475
|
+
files?: { memory: number; user: number };
|
|
1476
|
+
builtin_files?: { memory: number; user: number };
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
// ── System stats / checkpoints / hooks / curator / portal types ────────────
|
|
1480
|
+
|
|
1481
|
+
export interface SystemStats {
|
|
1482
|
+
os: string;
|
|
1483
|
+
os_release: string;
|
|
1484
|
+
os_version: string;
|
|
1485
|
+
platform: string;
|
|
1486
|
+
arch: string;
|
|
1487
|
+
hostname: string;
|
|
1488
|
+
python_version: string;
|
|
1489
|
+
python_impl: string;
|
|
1490
|
+
nastech_version: string;
|
|
1491
|
+
cpu_count: number | null;
|
|
1492
|
+
psutil: boolean;
|
|
1493
|
+
memory?: { total: number; available: number; used: number; percent: number };
|
|
1494
|
+
disk?: { total: number; used: number; free: number; percent: number };
|
|
1495
|
+
cpu_percent?: number;
|
|
1496
|
+
load_avg?: number[];
|
|
1497
|
+
uptime_seconds?: number;
|
|
1498
|
+
process?: { pid: number; rss: number; create_time: number; num_threads: number };
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
export interface CheckpointsSession {
|
|
1502
|
+
session: string;
|
|
1503
|
+
files: number;
|
|
1504
|
+
bytes: number;
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
export interface CheckpointsResponse {
|
|
1508
|
+
sessions: CheckpointsSession[];
|
|
1509
|
+
total_bytes: number;
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
export interface HookEntry {
|
|
1513
|
+
event: string;
|
|
1514
|
+
matcher?: string | null;
|
|
1515
|
+
command: string;
|
|
1516
|
+
timeout?: number | null;
|
|
1517
|
+
allowed: boolean;
|
|
1518
|
+
approved_at?: string | null;
|
|
1519
|
+
executable: boolean;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
export interface HooksResponse {
|
|
1523
|
+
hooks: HookEntry[];
|
|
1524
|
+
valid_events: string[];
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
export interface CuratorStatus {
|
|
1528
|
+
enabled: boolean;
|
|
1529
|
+
paused: boolean;
|
|
1530
|
+
interval_hours: number | null;
|
|
1531
|
+
last_run_at: string | null;
|
|
1532
|
+
min_idle_hours: number | null;
|
|
1533
|
+
stale_after_days: number | null;
|
|
1534
|
+
archive_after_days: number | null;
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1537
|
+
export interface PortalFeature {
|
|
1538
|
+
label: string;
|
|
1539
|
+
state: string;
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
export interface PortalStatus {
|
|
1543
|
+
logged_in: boolean;
|
|
1544
|
+
portal_url: string | null;
|
|
1545
|
+
inference_url: string | null;
|
|
1546
|
+
provider: string;
|
|
1547
|
+
subscription_url: string;
|
|
1548
|
+
features: PortalFeature[];
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
export interface CredentialPoolEntry {
|
|
1552
|
+
index: number;
|
|
1553
|
+
id: string | null;
|
|
1554
|
+
label: string | null;
|
|
1555
|
+
auth_type: string | null;
|
|
1556
|
+
source: string | null;
|
|
1557
|
+
priority: number;
|
|
1558
|
+
last_status: string | null;
|
|
1559
|
+
request_count: number;
|
|
1560
|
+
token_preview: string;
|
|
1561
|
+
has_refresh: boolean;
|
|
1562
|
+
}
|
|
1563
|
+
|
|
1564
|
+
export interface CredentialPoolProvider {
|
|
1565
|
+
provider: string;
|
|
1566
|
+
entries: CredentialPoolEntry[];
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
export interface UpdateCheckResponse {
|
|
1570
|
+
install_method: string;
|
|
1571
|
+
current_version: string;
|
|
1572
|
+
behind: number | null;
|
|
1573
|
+
update_available: boolean;
|
|
1574
|
+
can_apply: boolean;
|
|
1575
|
+
update_command: string;
|
|
1576
|
+
message: string | null;
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
export interface DebugShareResponse {
|
|
1580
|
+
ok: boolean;
|
|
1581
|
+
urls: Record<string, string>;
|
|
1582
|
+
failures: string[];
|
|
1583
|
+
redacted: boolean;
|
|
1584
|
+
auto_delete_seconds: number;
|
|
1585
|
+
}
|