@nextclaw/ui 0.11.0 → 0.11.1
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/CHANGELOG.md +8 -2
- package/dist/assets/{ChannelsList-BqsOYnXz.js → ChannelsList-CVPqrxns.js} +4 -4
- package/dist/assets/ChatPage-BO1VUrAY.js +37 -0
- package/dist/assets/{DocBrowser-BmL0QXBZ.js → DocBrowser-FBwg8iji.js} +1 -1
- package/dist/assets/{LogoBadge-C1HiPZPf.js → LogoBadge-BCmJfRT8.js} +1 -1
- package/dist/assets/MarketplacePage-DWxXUOCx.js +49 -0
- package/dist/assets/{McpMarketplacePage-CLHFnNBd.js → McpMarketplacePage-Bth9X_hu.js} +2 -2
- package/dist/assets/{ModelConfig-LQSR58tc.js → ModelConfig-PkSp_ioc.js} +1 -1
- package/dist/assets/ProvidersList-DVDge8wa.js +1 -0
- package/dist/assets/RemoteAccessPage-BVkzfEaL.js +1 -0
- package/dist/assets/RuntimeConfig-ByJs3khh.js +1 -0
- package/dist/assets/{SearchConfig-Chzo_JGs.js → SearchConfig-KZUAqYJN.js} +1 -1
- package/dist/assets/{SecretsConfig-CEIbjZYA.js → SecretsConfig-qwB_Y_Ka.js} +2 -2
- package/dist/assets/SessionsConfig-CGCl4UTr.js +2 -0
- package/dist/assets/index-CrilScMo.css +1 -0
- package/dist/assets/{index-j6A_-1b6.js → index-D41ntvb7.js} +6 -6
- package/dist/assets/{label-GACO2RzW.js → label-7JEFhkur.js} +1 -1
- package/dist/assets/ncp-session-adapter-BOqhkrc-.js +1 -0
- package/dist/assets/{page-layout-DjXaK3A3.js → page-layout-B7q511TE.js} +1 -1
- package/dist/assets/popover-CywJGmPr.js +1 -0
- package/dist/assets/security-config-zi2UxN5r.js +1 -0
- package/dist/assets/skeleton-qUJZQ03S.js +1 -0
- package/dist/assets/{status-dot-IWEBezqb.js → status-dot-BilwNdTT.js} +1 -1
- package/dist/assets/{switch-DCHAJSrA.js → switch-BLp2Pno1.js} +1 -1
- package/dist/assets/tabs-custom-CgIdQMGC.js +1 -0
- package/dist/assets/useConfirmDialog-BitswAkv.js +1 -0
- package/dist/assets/{vendor-CNhxtHCf.js → vendor-D_JxmsLV.js} +87 -87
- package/dist/index.html +3 -3
- package/package.json +4 -4
- package/src/App.test.tsx +42 -10
- package/src/App.tsx +5 -40
- package/src/api/api-base.test.ts +37 -0
- package/src/api/api-base.ts +0 -4
- package/src/api/config.ts +2 -270
- package/src/api/types.ts +0 -117
- package/src/components/chat/ChatPage.tsx +1 -11
- package/src/components/chat/ChatSidebar.test.tsx +1 -50
- package/src/components/chat/ChatSidebar.tsx +0 -5
- package/src/components/chat/README.md +2 -0
- package/src/components/chat/chat-attachment-upload-limit.test.ts +41 -0
- package/src/components/chat/chat-session-display.ts +9 -0
- package/src/components/chat/chat-session-label.service.ts +3 -12
- package/src/components/chat/chat-session-preference-sync.test.ts +10 -13
- package/src/components/chat/chat-stream/types.ts +4 -57
- package/src/components/chat/ncp/NcpChatPage.tsx +3 -3
- package/src/components/chat/useHydratedNcpAgent.test.tsx +77 -0
- package/src/components/config/README.md +2 -0
- package/src/components/config/SessionsConfig.tsx +152 -132
- package/src/hooks/use-auth.test.ts +3 -3
- package/src/hooks/use-auth.ts +16 -4
- package/src/hooks/use-realtime-query-bridge.ts +0 -24
- package/src/hooks/useConfig.ts +10 -137
- package/src/lib/session-run-status.ts +1 -63
- package/src/vite-env.d.ts +1 -0
- package/vite.config.ts +4 -4
- package/dist/assets/ChatPage-CJBYKR-Y.js +0 -38
- package/dist/assets/MarketplacePage-BIRP0NRS.js +0 -49
- package/dist/assets/ProvidersList-CwI-mxah.js +0 -1
- package/dist/assets/RemoteAccessPage-Cw5BqZb6.js +0 -1
- package/dist/assets/RuntimeConfig-DbowSRAb.js +0 -1
- package/dist/assets/SessionsConfig-BR8GfGWL.js +0 -2
- package/dist/assets/chat-message-CPG7zxRR.js +0 -3
- package/dist/assets/index-kaPUhd-8.css +0 -1
- package/dist/assets/popover-DTaFiTmU.js +0 -1
- package/dist/assets/security-config-Dk-yoKvK.js +0 -1
- package/dist/assets/skeleton-Dm2xOBSA.js +0 -1
- package/dist/assets/tabs-custom-DKSbDSB9.js +0 -1
- package/dist/assets/useConfirmDialog-ByJ8A8n7.js +0 -1
- package/src/api/config.stream.test.ts +0 -115
- package/src/components/chat/chat-chain.test.ts +0 -22
- package/src/components/chat/chat-chain.ts +0 -23
- package/src/components/chat/chat-page-data.ts +0 -171
- package/src/components/chat/chat-page-runtime.ts +0 -190
- package/src/components/chat/chat-stream/nextbot-parsers.ts +0 -52
- package/src/components/chat/chat-stream/nextbot-runtime-agent.ts +0 -413
- package/src/components/chat/chat-stream/stream-event-adapter.ts +0 -98
- package/src/components/chat/chat-stream/transport.ts +0 -253
- package/src/components/chat/legacy/LegacyChatPage.tsx +0 -223
- package/src/components/chat/managers/chat-input.manager.ts +0 -228
- package/src/components/chat/managers/chat-thread.manager.ts +0 -87
- package/src/components/chat/presenter/chat.presenter.ts +0 -32
- package/src/components/chat/useChatRuntimeController.ts +0 -134
package/dist/index.html
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-D41ntvb7.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-D_JxmsLV.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CrilScMo.css">
|
|
12
12
|
</head>
|
|
13
13
|
|
|
14
14
|
<body>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -28,11 +28,11 @@
|
|
|
28
28
|
"tailwind-merge": "^2.5.4",
|
|
29
29
|
"zod": "^3.23.8",
|
|
30
30
|
"zustand": "^5.0.2",
|
|
31
|
-
"@nextclaw/
|
|
32
|
-
"@nextclaw/ncp-react": "0.4.0",
|
|
31
|
+
"@nextclaw/agent-chat": "0.1.3",
|
|
33
32
|
"@nextclaw/ncp-http-agent-client": "0.3.4",
|
|
34
33
|
"@nextclaw/agent-chat-ui": "0.2.5",
|
|
35
|
-
"@nextclaw/
|
|
34
|
+
"@nextclaw/ncp": "0.4.0",
|
|
35
|
+
"@nextclaw/ncp-react": "0.4.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
38
|
"@testing-library/react": "^16.3.0",
|
package/src/App.test.tsx
CHANGED
|
@@ -1,41 +1,73 @@
|
|
|
1
1
|
import { render, screen } from '@testing-library/react';
|
|
2
2
|
import userEvent from '@testing-library/user-event';
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { MemoryRouter } from 'react-router-dom';
|
|
5
|
+
import { I18nProvider } from '@/components/providers/I18nProvider';
|
|
6
|
+
import { ThemeProvider } from '@/components/providers/ThemeProvider';
|
|
4
7
|
import AppContent from '@/App';
|
|
5
8
|
|
|
6
9
|
const mocks = vi.hoisted(() => ({
|
|
7
10
|
refetch: vi.fn(),
|
|
8
|
-
useAuthStatus: vi.fn()
|
|
11
|
+
useAuthStatus: vi.fn(),
|
|
12
|
+
isTransientAuthStatusBootstrapError: vi.fn()
|
|
9
13
|
}));
|
|
10
14
|
|
|
11
15
|
vi.mock('@/hooks/use-auth', () => ({
|
|
12
|
-
useAuthStatus: mocks.useAuthStatus
|
|
16
|
+
useAuthStatus: mocks.useAuthStatus,
|
|
17
|
+
isTransientAuthStatusBootstrapError: mocks.isTransientAuthStatusBootstrapError
|
|
13
18
|
}));
|
|
14
19
|
|
|
15
20
|
describe('App auth bootstrap', () => {
|
|
21
|
+
function renderApp() {
|
|
22
|
+
return render(
|
|
23
|
+
<ThemeProvider>
|
|
24
|
+
<I18nProvider>
|
|
25
|
+
<MemoryRouter initialEntries={['/chat']}>
|
|
26
|
+
<AppContent />
|
|
27
|
+
</MemoryRouter>
|
|
28
|
+
</I18nProvider>
|
|
29
|
+
</ThemeProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
16
33
|
beforeEach(() => {
|
|
17
34
|
mocks.refetch.mockReset();
|
|
18
35
|
mocks.useAuthStatus.mockReset();
|
|
36
|
+
mocks.isTransientAuthStatusBootstrapError.mockReset();
|
|
37
|
+
mocks.isTransientAuthStatusBootstrapError.mockReturnValue(false);
|
|
38
|
+
vi.useRealTimers();
|
|
19
39
|
});
|
|
20
40
|
|
|
21
|
-
it('
|
|
22
|
-
|
|
41
|
+
it('does not block the app shell for transient bootstrap failures', () => {
|
|
42
|
+
mocks.isTransientAuthStatusBootstrapError.mockReturnValue(true);
|
|
23
43
|
mocks.useAuthStatus.mockReturnValue({
|
|
24
44
|
isLoading: false,
|
|
25
45
|
isError: true,
|
|
26
46
|
isRefetching: false,
|
|
27
|
-
error: new Error('
|
|
47
|
+
error: new Error('Failed to fetch'),
|
|
28
48
|
refetch: mocks.refetch,
|
|
29
49
|
data: undefined
|
|
30
50
|
});
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
renderApp();
|
|
53
|
+
|
|
54
|
+
expect(screen.queryByRole('heading', { name: /waiting for the local ui service to start/i })).toBeNull();
|
|
55
|
+
expect(screen.queryByText('Failed to fetch')).toBeNull();
|
|
56
|
+
});
|
|
33
57
|
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
it('does not block the app shell for stable auth bootstrap failures', async () => {
|
|
59
|
+
mocks.useAuthStatus.mockReturnValue({
|
|
60
|
+
isLoading: false,
|
|
61
|
+
isError: true,
|
|
62
|
+
isRefetching: false,
|
|
63
|
+
error: new Error('Authentication required.'),
|
|
64
|
+
refetch: mocks.refetch,
|
|
65
|
+
data: undefined
|
|
66
|
+
});
|
|
36
67
|
|
|
37
|
-
|
|
68
|
+
renderApp();
|
|
38
69
|
|
|
39
|
-
expect(
|
|
70
|
+
expect(screen.queryByRole('heading', { name: /load authentication status/i })).toBeNull();
|
|
71
|
+
expect(screen.queryByText('Authentication required.')).toBeNull();
|
|
40
72
|
});
|
|
41
73
|
});
|
package/src/App.tsx
CHANGED
|
@@ -3,10 +3,8 @@ import { QueryClientProvider } from '@tanstack/react-query';
|
|
|
3
3
|
import { AccountPanel } from '@/account/components/account-panel';
|
|
4
4
|
import { appQueryClient } from '@/app-query-client';
|
|
5
5
|
import { LoginPage } from '@/components/auth/login-page';
|
|
6
|
-
import { Button } from '@/components/ui/button';
|
|
7
6
|
import { AppLayout } from '@/components/layout/AppLayout';
|
|
8
|
-
import { useAuthStatus } from '@/hooks/use-auth';
|
|
9
|
-
import { t } from '@/lib/i18n';
|
|
7
|
+
import { isTransientAuthStatusBootstrapError, useAuthStatus } from '@/hooks/use-auth';
|
|
10
8
|
import { useRealtimeQueryBridge } from '@/hooks/use-realtime-query-bridge';
|
|
11
9
|
import { AppPresenterProvider } from '@/presenter/app-presenter-context';
|
|
12
10
|
import { Toaster } from 'sonner';
|
|
@@ -33,29 +31,6 @@ function LazyRoute({ children }: { children: JSX.Element }) {
|
|
|
33
31
|
return <Suspense fallback={<RouteFallback />}>{children}</Suspense>;
|
|
34
32
|
}
|
|
35
33
|
|
|
36
|
-
function AuthBootstrapErrorState(props: {
|
|
37
|
-
message: string;
|
|
38
|
-
retrying: boolean;
|
|
39
|
-
onRetry: () => void;
|
|
40
|
-
}) {
|
|
41
|
-
return (
|
|
42
|
-
<main className="flex min-h-screen items-center justify-center bg-secondary px-6 py-10">
|
|
43
|
-
<div className="w-full max-w-lg rounded-3xl border border-gray-200 bg-white p-8 shadow-card">
|
|
44
|
-
<p className="text-xs font-semibold uppercase tracking-[0.24em] text-gray-500">{t('authBrand')}</p>
|
|
45
|
-
<h1 className="mt-3 text-2xl font-semibold text-gray-900">{t('authStatusLoadFailed')}</h1>
|
|
46
|
-
<p className="mt-3 text-sm leading-6 text-gray-600">
|
|
47
|
-
{props.message}
|
|
48
|
-
</p>
|
|
49
|
-
<div className="mt-6 flex gap-3">
|
|
50
|
-
<Button onClick={props.onRetry} disabled={props.retrying}>
|
|
51
|
-
{t('authRetryStatus')}
|
|
52
|
-
</Button>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
</main>
|
|
56
|
-
);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
34
|
function ProtectedApp() {
|
|
60
35
|
useRealtimeQueryBridge(appQueryClient);
|
|
61
36
|
|
|
@@ -95,21 +70,11 @@ function ProtectedApp() {
|
|
|
95
70
|
|
|
96
71
|
function AuthGate() {
|
|
97
72
|
const authStatus = useAuthStatus();
|
|
73
|
+
const isTransientBootstrapFailure =
|
|
74
|
+
authStatus.isError && isTransientAuthStatusBootstrapError(authStatus.error);
|
|
98
75
|
|
|
99
|
-
if (authStatus.isLoading && !authStatus.isError) {
|
|
100
|
-
return <
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
if (authStatus.isError) {
|
|
104
|
-
return (
|
|
105
|
-
<AuthBootstrapErrorState
|
|
106
|
-
message={authStatus.error instanceof Error ? authStatus.error.message : t('authStatusLoadFailed')}
|
|
107
|
-
retrying={authStatus.isRefetching}
|
|
108
|
-
onRetry={() => {
|
|
109
|
-
void authStatus.refetch();
|
|
110
|
-
}}
|
|
111
|
-
/>
|
|
112
|
-
);
|
|
76
|
+
if ((authStatus.isLoading && !authStatus.isError) || isTransientBootstrapFailure || authStatus.isError) {
|
|
77
|
+
return <ProtectedApp />;
|
|
113
78
|
}
|
|
114
79
|
|
|
115
80
|
if (authStatus.data?.enabled && !authStatus.data.authenticated) {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('API_BASE', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.resetModules();
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.unstubAllEnvs();
|
|
10
|
+
vi.unstubAllGlobals();
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('prefers explicit VITE_API_BASE when provided', async () => {
|
|
14
|
+
vi.stubEnv('VITE_API_BASE', 'https://api.example.com/');
|
|
15
|
+
vi.stubGlobal('window', {
|
|
16
|
+
location: {
|
|
17
|
+
origin: 'https://remote.claw.cool'
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const { API_BASE } = await import('@/api/api-base');
|
|
22
|
+
|
|
23
|
+
expect(API_BASE).toBe('https://api.example.com');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('falls back to window origin when no explicit API base is configured', async () => {
|
|
27
|
+
vi.stubGlobal('window', {
|
|
28
|
+
location: {
|
|
29
|
+
origin: 'https://remote.claw.cool'
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const { API_BASE } = await import('@/api/api-base');
|
|
34
|
+
|
|
35
|
+
expect(API_BASE).toBe('https://remote.claw.cool');
|
|
36
|
+
});
|
|
37
|
+
});
|
package/src/api/api-base.ts
CHANGED
package/src/api/config.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { api } from './client';
|
|
2
|
-
import { appClient } from '@/transport';
|
|
3
2
|
import type {
|
|
4
3
|
AuthEnabledUpdateRequest,
|
|
5
4
|
AuthLoginRequest,
|
|
@@ -30,22 +29,7 @@ import type {
|
|
|
30
29
|
SecretsView,
|
|
31
30
|
ConfigActionExecuteRequest,
|
|
32
31
|
ConfigActionExecuteResult,
|
|
33
|
-
SessionsListView,
|
|
34
|
-
SessionHistoryView,
|
|
35
|
-
SessionPatchUpdate,
|
|
36
|
-
ChatTurnRequest,
|
|
37
|
-
ChatTurnView,
|
|
38
|
-
ChatTurnStreamDeltaEvent,
|
|
39
|
-
ChatTurnStreamErrorEvent,
|
|
40
|
-
ChatTurnStreamReadyEvent,
|
|
41
|
-
ChatTurnStreamSessionEvent,
|
|
42
|
-
ChatCapabilitiesView,
|
|
43
32
|
ChatSessionTypesView,
|
|
44
|
-
ChatTurnStopRequest,
|
|
45
|
-
ChatTurnStopResult,
|
|
46
|
-
ChatRunListView,
|
|
47
|
-
ChatRunState,
|
|
48
|
-
ChatRunView,
|
|
49
33
|
CronListView,
|
|
50
34
|
CronEnableRequest,
|
|
51
35
|
CronRunRequest,
|
|
@@ -53,8 +37,8 @@ import type {
|
|
|
53
37
|
} from './types';
|
|
54
38
|
|
|
55
39
|
// GET /api/auth/status
|
|
56
|
-
export async function fetchAuthStatus(): Promise<AuthStatusView> {
|
|
57
|
-
const response = await api.get<AuthStatusView>('/api/auth/status', { timeoutMs: 5_000 });
|
|
40
|
+
export async function fetchAuthStatus(options: { timeoutMs?: number } = {}): Promise<AuthStatusView> {
|
|
41
|
+
const response = await api.get<AuthStatusView>('/api/auth/status', { timeoutMs: options.timeoutMs ?? 5_000 });
|
|
58
42
|
if (!response.ok) {
|
|
59
43
|
throw new Error(response.error.message);
|
|
60
44
|
}
|
|
@@ -317,216 +301,6 @@ export async function executeConfigAction(
|
|
|
317
301
|
return response.data;
|
|
318
302
|
}
|
|
319
303
|
|
|
320
|
-
|
|
321
|
-
// GET /api/sessions
|
|
322
|
-
export async function fetchSessions(params?: { q?: string; limit?: number; activeMinutes?: number }): Promise<SessionsListView> {
|
|
323
|
-
const query = new URLSearchParams();
|
|
324
|
-
if (params?.q?.trim()) {
|
|
325
|
-
query.set('q', params.q.trim());
|
|
326
|
-
}
|
|
327
|
-
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
328
|
-
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
329
|
-
}
|
|
330
|
-
if (typeof params?.activeMinutes === 'number' && Number.isFinite(params.activeMinutes)) {
|
|
331
|
-
query.set('activeMinutes', String(Math.max(0, Math.trunc(params.activeMinutes))));
|
|
332
|
-
}
|
|
333
|
-
const suffix = query.toString();
|
|
334
|
-
const response = await api.get<SessionsListView>(suffix ? '/api/sessions?' + suffix : '/api/sessions');
|
|
335
|
-
if (!response.ok) {
|
|
336
|
-
throw new Error(response.error.message);
|
|
337
|
-
}
|
|
338
|
-
return response.data;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// GET /api/sessions/:key/history
|
|
342
|
-
export async function fetchSessionHistory(key: string, limit = 200): Promise<SessionHistoryView> {
|
|
343
|
-
const response = await api.get<SessionHistoryView>(`/api/sessions/${encodeURIComponent(key)}/history?limit=${Math.max(1, Math.trunc(limit))}`);
|
|
344
|
-
if (!response.ok) {
|
|
345
|
-
throw new Error(response.error.message);
|
|
346
|
-
}
|
|
347
|
-
return response.data;
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
// PUT /api/sessions/:key
|
|
351
|
-
export async function updateSession(
|
|
352
|
-
key: string,
|
|
353
|
-
data: SessionPatchUpdate
|
|
354
|
-
): Promise<SessionHistoryView> {
|
|
355
|
-
const response = await api.put<SessionHistoryView>(`/api/sessions/${encodeURIComponent(key)}`, data);
|
|
356
|
-
if (!response.ok) {
|
|
357
|
-
throw new Error(response.error.message);
|
|
358
|
-
}
|
|
359
|
-
return response.data;
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
// DELETE /api/sessions/:key
|
|
363
|
-
export async function deleteSession(key: string): Promise<{ deleted: boolean }> {
|
|
364
|
-
const response = await api.delete<{ deleted: boolean }>(`/api/sessions/${encodeURIComponent(key)}`);
|
|
365
|
-
if (!response.ok) {
|
|
366
|
-
throw new Error(response.error.message);
|
|
367
|
-
}
|
|
368
|
-
return response.data;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
// POST /api/chat/turn
|
|
372
|
-
export async function sendChatTurn(data: ChatTurnRequest): Promise<ChatTurnView> {
|
|
373
|
-
const response = await api.post<ChatTurnView>('/api/chat/turn', data);
|
|
374
|
-
if (!response.ok) {
|
|
375
|
-
throw new Error(response.error.message);
|
|
376
|
-
}
|
|
377
|
-
return response.data;
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async function readSseStream(params: {
|
|
381
|
-
path: string;
|
|
382
|
-
method: 'GET' | 'POST';
|
|
383
|
-
body?: unknown;
|
|
384
|
-
signal?: AbortSignal;
|
|
385
|
-
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
386
|
-
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
387
|
-
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
388
|
-
}): Promise<{ sessionKey: string; reply: string }> {
|
|
389
|
-
let finalResult: { sessionKey: string; reply: string } | null = null;
|
|
390
|
-
let readySessionKey = '';
|
|
391
|
-
|
|
392
|
-
const session = appClient.openStream<ChatTurnView>({
|
|
393
|
-
method: params.method,
|
|
394
|
-
path: params.path,
|
|
395
|
-
...(params.body !== undefined ? { body: params.body } : {}),
|
|
396
|
-
signal: params.signal,
|
|
397
|
-
onEvent: (event) => {
|
|
398
|
-
if (event.name === 'ready') {
|
|
399
|
-
const ready = (event.payload ?? {}) as ChatTurnStreamReadyEvent;
|
|
400
|
-
if (typeof ready.sessionKey === 'string' && ready.sessionKey.trim()) {
|
|
401
|
-
readySessionKey = ready.sessionKey;
|
|
402
|
-
}
|
|
403
|
-
params.onReady(ready);
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (event.name === 'delta') {
|
|
408
|
-
params.onDelta((event.payload ?? { delta: '' }) as ChatTurnStreamDeltaEvent);
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
if (event.name === 'session_event') {
|
|
413
|
-
params.onSessionEvent({ data: event.payload as ChatTurnStreamSessionEvent['data'] });
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
if (event.name === 'final') {
|
|
418
|
-
const result = event.payload as ChatTurnView;
|
|
419
|
-
finalResult = {
|
|
420
|
-
sessionKey: typeof result?.sessionKey === 'string' && result.sessionKey.trim()
|
|
421
|
-
? result.sessionKey
|
|
422
|
-
: readySessionKey,
|
|
423
|
-
reply: typeof result?.reply === 'string' ? result.reply : ''
|
|
424
|
-
};
|
|
425
|
-
return;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
if (event.name === 'error') {
|
|
429
|
-
const errorPayload = (event.payload ?? {}) as ChatTurnStreamErrorEvent;
|
|
430
|
-
throw new Error((errorPayload.message ?? '').trim() || 'chat stream failed');
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
const result = await session.finished;
|
|
436
|
-
if (finalResult) {
|
|
437
|
-
return finalResult;
|
|
438
|
-
}
|
|
439
|
-
if (readySessionKey) {
|
|
440
|
-
return {
|
|
441
|
-
sessionKey: readySessionKey,
|
|
442
|
-
reply: typeof result?.reply === 'string' ? result.reply : ''
|
|
443
|
-
};
|
|
444
|
-
}
|
|
445
|
-
if (typeof result?.sessionKey === 'string' && result.sessionKey.trim()) {
|
|
446
|
-
return {
|
|
447
|
-
sessionKey: result.sessionKey,
|
|
448
|
-
reply: typeof result?.reply === 'string' ? result.reply : ''
|
|
449
|
-
};
|
|
450
|
-
}
|
|
451
|
-
throw new Error('chat stream ended without final event');
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
// POST /api/chat/turn/stream
|
|
455
|
-
export async function sendChatTurnStream(
|
|
456
|
-
data: ChatTurnRequest,
|
|
457
|
-
params: {
|
|
458
|
-
signal?: AbortSignal;
|
|
459
|
-
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
460
|
-
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
461
|
-
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
462
|
-
}
|
|
463
|
-
): Promise<{ sessionKey: string; reply: string }> {
|
|
464
|
-
return readSseStream({
|
|
465
|
-
path: '/api/chat/turn/stream',
|
|
466
|
-
method: 'POST',
|
|
467
|
-
body: data,
|
|
468
|
-
signal: params.signal,
|
|
469
|
-
onReady: params.onReady,
|
|
470
|
-
onDelta: params.onDelta,
|
|
471
|
-
onSessionEvent: params.onSessionEvent
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
// GET /api/chat/runs/:runId/stream
|
|
476
|
-
export async function streamChatRun(
|
|
477
|
-
data: {
|
|
478
|
-
runId: string;
|
|
479
|
-
fromEventIndex?: number;
|
|
480
|
-
},
|
|
481
|
-
params: {
|
|
482
|
-
signal?: AbortSignal;
|
|
483
|
-
onReady: (event: ChatTurnStreamReadyEvent) => void;
|
|
484
|
-
onDelta: (event: ChatTurnStreamDeltaEvent) => void;
|
|
485
|
-
onSessionEvent: (event: ChatTurnStreamSessionEvent) => void;
|
|
486
|
-
}
|
|
487
|
-
): Promise<{ sessionKey: string; reply: string }> {
|
|
488
|
-
const query = new URLSearchParams();
|
|
489
|
-
if (typeof data.fromEventIndex === 'number' && Number.isFinite(data.fromEventIndex)) {
|
|
490
|
-
query.set('fromEventIndex', String(Math.max(0, Math.trunc(data.fromEventIndex))));
|
|
491
|
-
}
|
|
492
|
-
const suffix = query.toString();
|
|
493
|
-
const path = `/api/chat/runs/${encodeURIComponent(data.runId)}/stream${suffix ? `?${suffix}` : ''}`;
|
|
494
|
-
return readSseStream({
|
|
495
|
-
path,
|
|
496
|
-
method: 'GET',
|
|
497
|
-
signal: params.signal,
|
|
498
|
-
onReady: params.onReady,
|
|
499
|
-
onDelta: params.onDelta,
|
|
500
|
-
onSessionEvent: params.onSessionEvent
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
// GET /api/chat/capabilities
|
|
505
|
-
export async function fetchChatCapabilities(params?: { sessionKey?: string; agentId?: string }): Promise<ChatCapabilitiesView> {
|
|
506
|
-
const query = new URLSearchParams();
|
|
507
|
-
if (params?.sessionKey?.trim()) {
|
|
508
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
509
|
-
}
|
|
510
|
-
if (params?.agentId?.trim()) {
|
|
511
|
-
query.set('agentId', params.agentId.trim());
|
|
512
|
-
}
|
|
513
|
-
const suffix = query.toString();
|
|
514
|
-
const response = await api.get<ChatCapabilitiesView>(suffix ? `/api/chat/capabilities?${suffix}` : '/api/chat/capabilities');
|
|
515
|
-
if (!response.ok) {
|
|
516
|
-
throw new Error(response.error.message);
|
|
517
|
-
}
|
|
518
|
-
return response.data;
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
// GET /api/chat/session-types
|
|
522
|
-
export async function fetchChatSessionTypes(): Promise<ChatSessionTypesView> {
|
|
523
|
-
const response = await api.get<ChatSessionTypesView>('/api/chat/session-types');
|
|
524
|
-
if (!response.ok) {
|
|
525
|
-
throw new Error(response.error.message);
|
|
526
|
-
}
|
|
527
|
-
return response.data;
|
|
528
|
-
}
|
|
529
|
-
|
|
530
304
|
// GET /api/ncp/session-types
|
|
531
305
|
export async function fetchNcpChatSessionTypes(): Promise<ChatSessionTypesView> {
|
|
532
306
|
const response = await api.get<ChatSessionTypesView>('/api/ncp/session-types');
|
|
@@ -536,48 +310,6 @@ export async function fetchNcpChatSessionTypes(): Promise<ChatSessionTypesView>
|
|
|
536
310
|
return response.data;
|
|
537
311
|
}
|
|
538
312
|
|
|
539
|
-
// POST /api/chat/turn/stop
|
|
540
|
-
export async function stopChatTurn(data: ChatTurnStopRequest): Promise<ChatTurnStopResult> {
|
|
541
|
-
const response = await api.post<ChatTurnStopResult>('/api/chat/turn/stop', data);
|
|
542
|
-
if (!response.ok) {
|
|
543
|
-
throw new Error(response.error.message);
|
|
544
|
-
}
|
|
545
|
-
return response.data;
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
// GET /api/chat/runs
|
|
549
|
-
export async function fetchChatRuns(params?: {
|
|
550
|
-
sessionKey?: string;
|
|
551
|
-
states?: ChatRunState[];
|
|
552
|
-
limit?: number;
|
|
553
|
-
}): Promise<ChatRunListView> {
|
|
554
|
-
const query = new URLSearchParams();
|
|
555
|
-
if (params?.sessionKey?.trim()) {
|
|
556
|
-
query.set('sessionKey', params.sessionKey.trim());
|
|
557
|
-
}
|
|
558
|
-
if (Array.isArray(params?.states) && params.states.length > 0) {
|
|
559
|
-
query.set('states', params.states.join(','));
|
|
560
|
-
}
|
|
561
|
-
if (typeof params?.limit === 'number' && Number.isFinite(params.limit)) {
|
|
562
|
-
query.set('limit', String(Math.max(0, Math.trunc(params.limit))));
|
|
563
|
-
}
|
|
564
|
-
const suffix = query.toString();
|
|
565
|
-
const response = await api.get<ChatRunListView>(suffix ? `/api/chat/runs?${suffix}` : '/api/chat/runs');
|
|
566
|
-
if (!response.ok) {
|
|
567
|
-
throw new Error(response.error.message);
|
|
568
|
-
}
|
|
569
|
-
return response.data;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// GET /api/chat/runs/:runId
|
|
573
|
-
export async function fetchChatRun(runId: string): Promise<ChatRunView> {
|
|
574
|
-
const response = await api.get<ChatRunView>(`/api/chat/runs/${encodeURIComponent(runId)}`);
|
|
575
|
-
if (!response.ok) {
|
|
576
|
-
throw new Error(response.error.message);
|
|
577
|
-
}
|
|
578
|
-
return response.data;
|
|
579
|
-
}
|
|
580
|
-
|
|
581
313
|
// GET /api/cron
|
|
582
314
|
export async function fetchCronJobs(params?: { all?: boolean }): Promise<CronListView> {
|
|
583
315
|
const query = new URLSearchParams();
|
package/src/api/types.ts
CHANGED
|
@@ -211,11 +211,6 @@ export type SessionEntryView = {
|
|
|
211
211
|
lastTimestamp?: string;
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
-
export type SessionsListView = {
|
|
215
|
-
sessions: SessionEntryView[];
|
|
216
|
-
total: number;
|
|
217
|
-
};
|
|
218
|
-
|
|
219
214
|
export type SessionMessageView = {
|
|
220
215
|
role: string;
|
|
221
216
|
content: unknown;
|
|
@@ -233,17 +228,6 @@ export type SessionEventView = {
|
|
|
233
228
|
message?: SessionMessageView;
|
|
234
229
|
};
|
|
235
230
|
|
|
236
|
-
export type SessionHistoryView = {
|
|
237
|
-
key: string;
|
|
238
|
-
totalMessages: number;
|
|
239
|
-
totalEvents: number;
|
|
240
|
-
sessionType: string;
|
|
241
|
-
sessionTypeMutable: boolean;
|
|
242
|
-
metadata: Record<string, unknown>;
|
|
243
|
-
messages: SessionMessageView[];
|
|
244
|
-
events: SessionEventView[];
|
|
245
|
-
};
|
|
246
|
-
|
|
247
231
|
export type NcpSessionSummaryView = NcpSessionSummary;
|
|
248
232
|
|
|
249
233
|
export type NcpSessionsListView = {
|
|
@@ -282,112 +266,12 @@ export type SessionPatchUpdate = {
|
|
|
282
266
|
clearHistory?: boolean;
|
|
283
267
|
};
|
|
284
268
|
|
|
285
|
-
export type ChatTurnRequest = {
|
|
286
|
-
message: string;
|
|
287
|
-
sessionKey?: string;
|
|
288
|
-
agentId?: string;
|
|
289
|
-
channel?: string;
|
|
290
|
-
chatId?: string;
|
|
291
|
-
model?: string;
|
|
292
|
-
metadata?: Record<string, unknown>;
|
|
293
|
-
};
|
|
294
|
-
|
|
295
|
-
export type ChatTurnView = {
|
|
296
|
-
reply: string;
|
|
297
|
-
sessionKey: string;
|
|
298
|
-
agentId?: string;
|
|
299
|
-
model?: string;
|
|
300
|
-
requestedAt: string;
|
|
301
|
-
completedAt: string;
|
|
302
|
-
durationMs: number;
|
|
303
|
-
};
|
|
304
|
-
|
|
305
|
-
export type ChatTurnStreamReadyEvent = {
|
|
306
|
-
sessionKey: string;
|
|
307
|
-
requestedAt?: string;
|
|
308
|
-
runId?: string;
|
|
309
|
-
stopSupported?: boolean;
|
|
310
|
-
stopReason?: string;
|
|
311
|
-
};
|
|
312
|
-
|
|
313
269
|
export type {
|
|
314
270
|
ChatSessionTypeCtaView,
|
|
315
271
|
ChatSessionTypeOptionView,
|
|
316
272
|
ChatSessionTypesView,
|
|
317
273
|
} from './chat-session-type.types';
|
|
318
274
|
|
|
319
|
-
export type ChatTurnStreamDeltaEvent = {
|
|
320
|
-
delta: string;
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
export type ChatTurnStreamSessionEvent = {
|
|
324
|
-
data: SessionEventView;
|
|
325
|
-
};
|
|
326
|
-
|
|
327
|
-
export type ChatTurnStreamErrorEvent = {
|
|
328
|
-
code?: string;
|
|
329
|
-
message?: string;
|
|
330
|
-
};
|
|
331
|
-
|
|
332
|
-
export type ChatCapabilitiesView = {
|
|
333
|
-
stopSupported: boolean;
|
|
334
|
-
stopReason?: string;
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
export type ChatCommandOptionView = {
|
|
338
|
-
name: string;
|
|
339
|
-
description: string;
|
|
340
|
-
type: 'string' | 'boolean' | 'number';
|
|
341
|
-
required?: boolean;
|
|
342
|
-
};
|
|
343
|
-
|
|
344
|
-
export type ChatCommandView = {
|
|
345
|
-
name: string;
|
|
346
|
-
description: string;
|
|
347
|
-
options?: ChatCommandOptionView[];
|
|
348
|
-
};
|
|
349
|
-
|
|
350
|
-
export type ChatCommandsView = {
|
|
351
|
-
commands: ChatCommandView[];
|
|
352
|
-
total: number;
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
export type ChatTurnStopRequest = {
|
|
356
|
-
runId: string;
|
|
357
|
-
sessionKey?: string;
|
|
358
|
-
agentId?: string;
|
|
359
|
-
};
|
|
360
|
-
|
|
361
|
-
export type ChatTurnStopResult = {
|
|
362
|
-
stopped: boolean;
|
|
363
|
-
runId: string;
|
|
364
|
-
sessionKey?: string;
|
|
365
|
-
reason?: string;
|
|
366
|
-
};
|
|
367
|
-
|
|
368
|
-
export type ChatRunState = 'queued' | 'running' | 'completed' | 'failed' | 'aborted';
|
|
369
|
-
|
|
370
|
-
export type ChatRunView = {
|
|
371
|
-
runId: string;
|
|
372
|
-
sessionKey: string;
|
|
373
|
-
agentId?: string;
|
|
374
|
-
model?: string;
|
|
375
|
-
state: ChatRunState;
|
|
376
|
-
requestedAt: string;
|
|
377
|
-
startedAt?: string;
|
|
378
|
-
completedAt?: string;
|
|
379
|
-
stopSupported: boolean;
|
|
380
|
-
stopReason?: string;
|
|
381
|
-
error?: string;
|
|
382
|
-
reply?: string;
|
|
383
|
-
eventCount: number;
|
|
384
|
-
};
|
|
385
|
-
|
|
386
|
-
export type ChatRunListView = {
|
|
387
|
-
runs: ChatRunView[];
|
|
388
|
-
total: number;
|
|
389
|
-
};
|
|
390
|
-
|
|
391
275
|
export type CronScheduleView =
|
|
392
276
|
| { kind: "at"; atMs?: number | null }
|
|
393
277
|
| { kind: "every"; everyMs?: number | null }
|
|
@@ -684,7 +568,6 @@ export type ConfigActionExecuteResult = {
|
|
|
684
568
|
// WebSocket events
|
|
685
569
|
export type WsEvent =
|
|
686
570
|
| { type: 'config.updated'; payload: { path: string } }
|
|
687
|
-
| { type: 'run.updated'; payload: { run: ChatRunView } }
|
|
688
571
|
| { type: 'session.updated'; payload: { sessionKey: string } }
|
|
689
572
|
| { type: 'config.reload.started'; payload?: Record<string, unknown> }
|
|
690
573
|
| { type: 'config.reload.finished'; payload?: Record<string, unknown> }
|