@nextclaw/ui 0.12.20-beta.0 → 0.12.20-beta.2

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.
Files changed (90) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/assets/api-BcqDx0tm.js +15 -0
  3. package/dist/assets/app-manager-provider-DVYBjif-.js +1 -0
  4. package/dist/assets/{app-navigation.config-BORqHkbN.js → app-navigation.config-BOVDFMnp.js} +1 -1
  5. package/dist/assets/{channels-list-page-sISO_4Yj.js → channels-list-page-CsoI4OJm.js} +2 -2
  6. package/dist/assets/{chat-ChCu7LQD.js → chat-ahMH_i_K.js} +6 -6
  7. package/dist/assets/chat-page-BxlXY-MB.js +1 -0
  8. package/dist/assets/chunk-JZWAC4HX-u4uYphxM.js +3 -0
  9. package/dist/assets/{desktop-update-config-BfJ5iSeY.js → desktop-update-config-CD6-2PfI.js} +1 -1
  10. package/dist/assets/{dialog-B-CXiFPZ.js → dialog-csshWetU.js} +1 -1
  11. package/dist/assets/{dist-DYVfg3q5.js → dist-Bl94Ahwx.js} +1 -1
  12. package/dist/assets/{es2015-BXroVnPi.js → es2015-JCM5-KtW.js} +1 -1
  13. package/dist/assets/index-CM-57d8J.js +2 -0
  14. package/dist/assets/index-D8MKmXtO.css +1 -0
  15. package/dist/assets/marketplace-page-DJGDpTAo.js +1 -0
  16. package/dist/assets/{marketplace-page-C9oZ01rM.js → marketplace-page-DxlxHCFm.js} +2 -2
  17. package/dist/assets/{mcp-marketplace-page-DuEixgSs.js → mcp-marketplace-page-5UjYRWOR.js} +2 -2
  18. package/dist/assets/mcp-marketplace-page-C1XaHZZO.js +1 -0
  19. package/dist/assets/{model-config-mfhqEZBG.js → model-config-PccJ9XyH.js} +1 -1
  20. package/dist/assets/{notice-card-CozHB03G.js → notice-card-CCgk6FvF.js} +1 -1
  21. package/dist/assets/{popover-CPUPma-w.js → popover-YAsxDBhY.js} +1 -1
  22. package/dist/assets/{provider-scoped-model-input-CL9sti2I.js → provider-scoped-model-input-CzpF7cug.js} +1 -1
  23. package/dist/assets/{providers-list-HPmL2akJ.js → providers-list-8qDMER8o.js} +1 -1
  24. package/dist/assets/remote-D4TtLPAp.js +1 -0
  25. package/dist/assets/runtime-config-page-DWJHrV7H.js +1 -0
  26. package/dist/assets/{search-config-Bcnk9VlL.js → search-config-D3a65l3r.js} +1 -1
  27. package/dist/assets/{secrets-config-Dde-5Y1w.js → secrets-config-CoMlR_7i.js} +2 -2
  28. package/dist/assets/{select-BELPuXLW.js → select-DIZrwsKU.js} +1 -1
  29. package/dist/assets/{sessions-config-page-CG49_0Z6.js → sessions-config-page-QjH5tgjr.js} +2 -2
  30. package/dist/assets/{setting-row-D5DtT6Ny.js → setting-row-DiQyrE81.js} +1 -1
  31. package/dist/assets/{tag-chip-D9BWWgYg.js → tag-chip-C3wDBe_-.js} +1 -1
  32. package/dist/assets/theme-provider-W704JWF8.js +1 -0
  33. package/dist/assets/{tooltip-CI0rpNee.js → tooltip-Dq5Xehpk.js} +1 -1
  34. package/dist/assets/use-config-BQJjq1mP.js +1 -0
  35. package/dist/assets/{use-confirm-dialog-hbynwWf2.js → use-confirm-dialog-DBoV5n5P.js} +1 -1
  36. package/dist/assets/{use-infinite-scroll-loader-Cw5qQr3-.js → use-infinite-scroll-loader-JAicqVC5.js} +1 -1
  37. package/dist/assets/{use-viewport-layout-CWHVDC6z.js → use-viewport-layout-BX3XqzJ4.js} +1 -1
  38. package/dist/index.html +17 -17
  39. package/package.json +8 -6
  40. package/src/app/hooks/use-realtime-query-bridge.ts +5 -5
  41. package/src/features/channels/components/config/channel-form.tsx +3 -3
  42. package/src/features/chat/hooks/use-ncp-chat-page-data.ts +7 -6
  43. package/src/features/chat/pages/ncp-chat-page.test.ts +22 -8
  44. package/src/features/chat/utils/chat-session-preference-governance.utils.test.tsx +114 -0
  45. package/src/features/chat/utils/chat-session-preference-governance.utils.ts +30 -36
  46. package/src/shared/components/common/brand-header.test.tsx +7 -2
  47. package/src/shared/components/common/brand-header.tsx +34 -4
  48. package/src/shared/lib/api/index.ts +12 -12
  49. package/src/shared/lib/api/ncp-session.test.ts +17 -18
  50. package/src/shared/lib/api/raw-client.utils.ts +3 -126
  51. package/src/shared/lib/api/services/agents.service.ts +18 -0
  52. package/src/shared/lib/api/services/channel-auth.service.ts +21 -0
  53. package/src/shared/lib/api/{client.ts → services/client.service.ts} +45 -1
  54. package/src/shared/lib/api/services/config.service.ts +171 -0
  55. package/src/shared/lib/api/services/marketplace.service.ts +66 -0
  56. package/src/shared/lib/api/services/mcp-marketplace.service.ts +70 -0
  57. package/src/shared/lib/api/services/ncp-attachments.service.ts +14 -0
  58. package/src/shared/lib/api/services/ncp-session.service.ts +39 -0
  59. package/src/shared/lib/api/services/remote.service.ts +50 -0
  60. package/src/shared/lib/api/services/runtime-control.service.ts +18 -0
  61. package/src/shared/lib/api/services/runtime-update.service.ts +26 -0
  62. package/src/shared/lib/api/services/server-path.service.ts +16 -0
  63. package/src/shared/lib/transport/index.ts +1 -0
  64. package/src/shared/lib/transport/local-transport.service.ts +24 -4
  65. package/src/shared/lib/transport/remote-transport.service.ts +1 -1
  66. package/src/shared/lib/transport/request-raw-api-response.utils.ts +133 -0
  67. package/src/shared/lib/transport/transport.types.ts +8 -2
  68. package/dist/assets/api-C412zuay.js +0 -15
  69. package/dist/assets/app-manager-provider-Cm-KiZZG.js +0 -1
  70. package/dist/assets/chat-page-BCaNZJGT.js +0 -1
  71. package/dist/assets/chunk-JZWAC4HX-DvbcIVPf.js +0 -3
  72. package/dist/assets/index-CUmk8xFK.css +0 -1
  73. package/dist/assets/index-CqPDhosM.js +0 -2
  74. package/dist/assets/marketplace-page-C8uaWkfd.js +0 -1
  75. package/dist/assets/mcp-marketplace-page-rNqr6ZpD.js +0 -1
  76. package/dist/assets/remote-oDlAdgVA.js +0 -1
  77. package/dist/assets/runtime-config-page-BCshTAAE.js +0 -1
  78. package/dist/assets/theme-provider-DeBrTglS.js +0 -1
  79. package/dist/assets/use-config-CrWZ_TSF.js +0 -1
  80. package/src/shared/lib/api/agents.ts +0 -34
  81. package/src/shared/lib/api/channel-auth.ts +0 -35
  82. package/src/shared/lib/api/config.ts +0 -362
  83. package/src/shared/lib/api/marketplace.ts +0 -156
  84. package/src/shared/lib/api/mcp-marketplace.ts +0 -138
  85. package/src/shared/lib/api/ncp-attachments.ts +0 -41
  86. package/src/shared/lib/api/ncp-session.ts +0 -78
  87. package/src/shared/lib/api/remote.ts +0 -86
  88. package/src/shared/lib/api/runtime-control.ts +0 -34
  89. package/src/shared/lib/api/runtime-update.service.ts +0 -50
  90. package/src/shared/lib/api/server-path.ts +0 -46
@@ -0,0 +1,171 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type {
3
+ AuthEnabledUpdateRequest,
4
+ AuthLoginRequest,
5
+ AuthPasswordUpdateRequest,
6
+ AuthSetupRequest,
7
+ AuthStatusView,
8
+ AppMetaView,
9
+ BootstrapStatusView,
10
+ ConfigView,
11
+ ConfigMetaView,
12
+ ConfigSchemaResponse,
13
+ ProviderConfigView,
14
+ ChannelConfigUpdate,
15
+ ProviderConfigUpdate,
16
+ ProviderConnectionTestRequest,
17
+ ProviderConnectionTestResult,
18
+ ProviderAuthStartRequest,
19
+ ProviderAuthStartResult,
20
+ ProviderAuthPollRequest,
21
+ ProviderAuthPollResult,
22
+ ProviderAuthImportResult,
23
+ SearchConfigUpdate,
24
+ SearchConfigView,
25
+ ProviderCreateRequest,
26
+ ProviderCreateResult,
27
+ ProviderDeleteResult,
28
+ RuntimeConfigUpdate,
29
+ SecretsConfigUpdate,
30
+ SecretsView,
31
+ ConfigActionExecuteRequest,
32
+ ConfigActionExecuteResult,
33
+ ChatSessionTypesView,
34
+ CronListView,
35
+ CronEnableRequest,
36
+ CronRunRequest,
37
+ CronActionResult
38
+ } from '@/shared/lib/api/types';
39
+
40
+ export async function fetchAuthStatus(options: { timeoutMs?: number } = {}): Promise<AuthStatusView> {
41
+ return await nextclawClient.auth.fetchStatus({ timeoutMs: options.timeoutMs ?? 5_000 });
42
+ }
43
+
44
+ export async function setupAuth(data: AuthSetupRequest): Promise<AuthStatusView> {
45
+ return await nextclawClient.auth.setup(data);
46
+ }
47
+
48
+ export async function loginAuth(data: AuthLoginRequest): Promise<AuthStatusView> {
49
+ return await nextclawClient.auth.login(data);
50
+ }
51
+
52
+ export async function logoutAuth(): Promise<{ success: boolean }> {
53
+ return await nextclawClient.auth.logout();
54
+ }
55
+
56
+ export async function updateAuthPassword(data: AuthPasswordUpdateRequest): Promise<AuthStatusView> {
57
+ return await nextclawClient.auth.updatePassword(data);
58
+ }
59
+
60
+ export async function updateAuthEnabled(data: AuthEnabledUpdateRequest): Promise<AuthStatusView> {
61
+ return await nextclawClient.auth.updateEnabled(data);
62
+ }
63
+
64
+ export async function fetchAppMeta(): Promise<AppMetaView> {
65
+ return await nextclawClient.app.fetchMeta();
66
+ }
67
+
68
+ export async function fetchBootstrapStatus(): Promise<BootstrapStatusView> {
69
+ return await nextclawClient.app.fetchBootstrapStatus();
70
+ }
71
+
72
+ export async function fetchConfig(): Promise<ConfigView> {
73
+ return await nextclawClient.config.fetch();
74
+ }
75
+
76
+ export async function fetchConfigMeta(): Promise<ConfigMetaView> {
77
+ return await nextclawClient.config.fetchMeta();
78
+ }
79
+
80
+ export async function fetchConfigSchema(): Promise<ConfigSchemaResponse> {
81
+ return await nextclawClient.config.fetchSchema();
82
+ }
83
+
84
+ export async function updateModel(data: { model: string; workspace?: string }): Promise<{ model: string; workspace?: string }> {
85
+ return await nextclawClient.config.updateModel(data);
86
+ }
87
+
88
+ export async function updateSearch(data: SearchConfigUpdate): Promise<SearchConfigView> {
89
+ return await nextclawClient.config.updateSearch(data);
90
+ }
91
+
92
+ export async function updateProvider(provider: string, data: ProviderConfigUpdate): Promise<ProviderConfigView> {
93
+ return await nextclawClient.config.updateProvider(provider, data);
94
+ }
95
+
96
+ export async function createProvider(data: ProviderCreateRequest = {}): Promise<ProviderCreateResult> {
97
+ return await nextclawClient.config.createProvider(data);
98
+ }
99
+
100
+ export async function deleteProvider(provider: string): Promise<ProviderDeleteResult> {
101
+ return await nextclawClient.config.deleteProvider(provider);
102
+ }
103
+
104
+ export async function testProviderConnection(
105
+ provider: string,
106
+ data: ProviderConnectionTestRequest
107
+ ): Promise<ProviderConnectionTestResult> {
108
+ return await nextclawClient.config.testProviderConnection(provider, data);
109
+ }
110
+
111
+ export async function startProviderAuth(
112
+ provider: string,
113
+ data: ProviderAuthStartRequest = {}
114
+ ): Promise<ProviderAuthStartResult> {
115
+ return await nextclawClient.config.startProviderAuth(provider, data);
116
+ }
117
+
118
+ export async function pollProviderAuth(
119
+ provider: string,
120
+ data: ProviderAuthPollRequest
121
+ ): Promise<ProviderAuthPollResult> {
122
+ return await nextclawClient.config.pollProviderAuth(provider, data);
123
+ }
124
+
125
+ export async function importProviderAuthFromCli(provider: string): Promise<ProviderAuthImportResult> {
126
+ return await nextclawClient.config.importProviderAuthFromCli(provider);
127
+ }
128
+
129
+ export async function updateChannel(
130
+ channel: string,
131
+ data: ChannelConfigUpdate
132
+ ): Promise<Record<string, unknown>> {
133
+ return await nextclawClient.config.updateChannel(channel, data);
134
+ }
135
+
136
+ export async function updateRuntime(
137
+ data: RuntimeConfigUpdate
138
+ ): Promise<Pick<ConfigView, 'agents' | 'bindings' | 'session'>> {
139
+ return await nextclawClient.config.updateRuntime(data) as Pick<ConfigView, 'agents' | 'bindings' | 'session'>;
140
+ }
141
+
142
+ export async function updateSecrets(data: SecretsConfigUpdate): Promise<SecretsView> {
143
+ return await nextclawClient.config.updateSecrets(data);
144
+ }
145
+
146
+ export async function executeConfigAction(
147
+ actionId: string,
148
+ data: ConfigActionExecuteRequest
149
+ ): Promise<ConfigActionExecuteResult> {
150
+ return await nextclawClient.config.executeAction(actionId, data);
151
+ }
152
+
153
+ export async function fetchNcpChatSessionTypes(): Promise<ChatSessionTypesView> {
154
+ return await nextclawClient.config.fetchChatSessionTypes();
155
+ }
156
+
157
+ export async function fetchCronJobs(params?: { all?: boolean }): Promise<CronListView> {
158
+ return await nextclawClient.config.fetchCronJobs(params);
159
+ }
160
+
161
+ export async function deleteCronJob(id: string): Promise<{ deleted: boolean }> {
162
+ return await nextclawClient.config.deleteCronJob(id);
163
+ }
164
+
165
+ export async function setCronJobEnabled(id: string, data: CronEnableRequest): Promise<CronActionResult> {
166
+ return await nextclawClient.config.setCronJobEnabled(id, data);
167
+ }
168
+
169
+ export async function runCronJob(id: string, data: CronRunRequest): Promise<CronActionResult> {
170
+ return await nextclawClient.config.runCronJob(id, data);
171
+ }
@@ -0,0 +1,66 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type {
3
+ MarketplaceInstallRequest,
4
+ MarketplaceInstallResult,
5
+ MarketplaceManageRequest,
6
+ MarketplaceManageResult,
7
+ MarketplaceInstalledView,
8
+ MarketplacePluginContentView,
9
+ MarketplaceItemType,
10
+ MarketplaceSkillContentView,
11
+ MarketplaceItemView,
12
+ MarketplaceListView,
13
+ MarketplaceRecommendationView,
14
+ MarketplaceSort
15
+ } from '@/shared/lib/api/types';
16
+
17
+ export type MarketplaceListParams = {
18
+ type: MarketplaceItemType;
19
+ q?: string;
20
+ tag?: string;
21
+ sort?: MarketplaceSort;
22
+ page?: number;
23
+ pageSize?: number;
24
+ };
25
+
26
+ export async function fetchMarketplaceItems(params: MarketplaceListParams): Promise<MarketplaceListView> {
27
+ return await nextclawClient.marketplace.fetchItems(params);
28
+ }
29
+
30
+ export async function fetchMarketplaceItem(slug: string, type: MarketplaceItemType): Promise<MarketplaceItemView> {
31
+ return await nextclawClient.marketplace.fetchItem(slug, type);
32
+ }
33
+
34
+ export async function fetchMarketplaceSkillContent(slug: string): Promise<MarketplaceSkillContentView> {
35
+ return await nextclawClient.marketplace.fetchSkillContent(slug);
36
+ }
37
+
38
+ export async function fetchMarketplacePluginContent(slug: string): Promise<MarketplacePluginContentView> {
39
+ return await nextclawClient.marketplace.fetchPluginContent(slug);
40
+ }
41
+
42
+ export async function fetchMarketplaceRecommendations(
43
+ type: MarketplaceItemType,
44
+ params: {
45
+ scene?: string;
46
+ limit?: number;
47
+ } = {}
48
+ ): Promise<MarketplaceRecommendationView> {
49
+ return await nextclawClient.marketplace.fetchRecommendations(type, params);
50
+ }
51
+
52
+ export async function installMarketplaceItem(request: MarketplaceInstallRequest): Promise<MarketplaceInstallResult> {
53
+ return await nextclawClient.marketplace.install(
54
+ request as Parameters<typeof nextclawClient.marketplace.install>[0]
55
+ ) as MarketplaceInstallResult;
56
+ }
57
+
58
+ export async function fetchMarketplaceInstalled(type: MarketplaceItemType): Promise<MarketplaceInstalledView> {
59
+ return await nextclawClient.marketplace.fetchInstalled(type);
60
+ }
61
+
62
+ export async function manageMarketplaceItem(request: MarketplaceManageRequest): Promise<MarketplaceManageResult> {
63
+ return await nextclawClient.marketplace.manage(
64
+ request as Parameters<typeof nextclawClient.marketplace.manage>[0]
65
+ ) as MarketplaceManageResult;
66
+ }
@@ -0,0 +1,70 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type {
3
+ MarketplaceInstalledView,
4
+ MarketplaceItemView,
5
+ MarketplaceListView,
6
+ MarketplaceMcpContentView,
7
+ MarketplaceMcpDoctorResult,
8
+ MarketplaceRecommendationView,
9
+ MarketplaceSort
10
+ } from '@/shared/lib/api/types';
11
+
12
+ export type McpMarketplaceListParams = {
13
+ q?: string;
14
+ tag?: string;
15
+ sort?: MarketplaceSort;
16
+ page?: number;
17
+ pageSize?: number;
18
+ };
19
+
20
+ export async function fetchMcpMarketplaceItems(params: McpMarketplaceListParams): Promise<MarketplaceListView> {
21
+ return await nextclawClient.mcpMarketplace.fetchItems(params);
22
+ }
23
+
24
+ export async function fetchMcpMarketplaceInstalled(): Promise<MarketplaceInstalledView> {
25
+ return await nextclawClient.mcpMarketplace.fetchInstalled();
26
+ }
27
+
28
+ export async function fetchMcpMarketplaceItem(slug: string): Promise<MarketplaceItemView> {
29
+ return await nextclawClient.mcpMarketplace.fetchItem(slug);
30
+ }
31
+
32
+ export async function fetchMcpMarketplaceContent(slug: string): Promise<MarketplaceMcpContentView> {
33
+ return await nextclawClient.mcpMarketplace.fetchContent(slug);
34
+ }
35
+
36
+ export async function fetchMcpMarketplaceRecommendations(params: {
37
+ scene?: string;
38
+ limit?: number;
39
+ } = {}): Promise<MarketplaceRecommendationView> {
40
+ return await nextclawClient.mcpMarketplace.fetchRecommendations(params);
41
+ }
42
+
43
+ export async function installMcpMarketplaceItem(request: {
44
+ spec: string;
45
+ name?: string;
46
+ enabled?: boolean;
47
+ allAgents?: boolean;
48
+ agents?: string[];
49
+ inputs?: Record<string, string>;
50
+ }): Promise<{ type: 'mcp'; spec: string; name?: string; message: string; output?: string }> {
51
+ return await nextclawClient.mcpMarketplace.install({
52
+ type: 'mcp',
53
+ ...request
54
+ });
55
+ }
56
+
57
+ export async function manageMcpMarketplaceItem(request: {
58
+ action: 'enable' | 'disable' | 'remove';
59
+ id?: string;
60
+ spec?: string;
61
+ }): Promise<{ type: 'mcp'; action: 'enable' | 'disable' | 'remove'; id: string; message: string; output?: string }> {
62
+ return await nextclawClient.mcpMarketplace.manage({
63
+ type: 'mcp',
64
+ ...request
65
+ });
66
+ }
67
+
68
+ export async function doctorMcpMarketplaceItem(name: string): Promise<MarketplaceMcpDoctorResult> {
69
+ return await nextclawClient.mcpMarketplace.doctor(name);
70
+ }
@@ -0,0 +1,14 @@
1
+ import type { NcpDraftAttachment } from "@nextclaw/ncp-react";
2
+ import { nextclawClient } from "./client.service";
3
+
4
+ export async function uploadNcpAssets(files: File[]): Promise<NcpDraftAttachment[]> {
5
+ const payload = await nextclawClient.sessions.uploadAssets(files);
6
+ return payload.assets.map((asset) => ({
7
+ id: asset.id,
8
+ name: asset.name,
9
+ mimeType: asset.mimeType,
10
+ sizeBytes: asset.sizeBytes,
11
+ assetUri: asset.assetUri,
12
+ url: asset.url,
13
+ }));
14
+ }
@@ -0,0 +1,39 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type {
3
+ NcpSessionMessagesView,
4
+ NcpSessionSkillsView,
5
+ NcpSessionsListView,
6
+ NcpSessionSummaryView,
7
+ SessionPatchUpdate
8
+ } from '@/shared/lib/api/types';
9
+
10
+ // GET /api/ncp/sessions
11
+ export async function fetchNcpSessions(params?: { limit?: number }): Promise<NcpSessionsListView> {
12
+ return await nextclawClient.sessions.list(params) as NcpSessionsListView;
13
+ }
14
+
15
+ // GET /api/ncp/sessions/:sessionId/messages
16
+ export async function fetchNcpSessionMessages(sessionId: string, limit = 200): Promise<NcpSessionMessagesView> {
17
+ return await nextclawClient.sessions.listMessages(sessionId, limit) as NcpSessionMessagesView;
18
+ }
19
+
20
+ // GET /api/ncp/sessions/:sessionId/skills
21
+ export async function fetchNcpSessionSkills(
22
+ sessionId: string,
23
+ params?: { projectRoot?: string | null }
24
+ ): Promise<NcpSessionSkillsView> {
25
+ return await nextclawClient.sessions.listSkills(sessionId, params);
26
+ }
27
+
28
+ // PUT /api/ncp/sessions/:sessionId
29
+ export async function updateNcpSession(
30
+ sessionId: string,
31
+ data: SessionPatchUpdate
32
+ ): Promise<NcpSessionSummaryView> {
33
+ return await nextclawClient.sessions.update(sessionId, data) as NcpSessionSummaryView;
34
+ }
35
+
36
+ // DELETE /api/ncp/sessions/:sessionId
37
+ export async function deleteNcpSession(sessionId: string): Promise<{ deleted: boolean; sessionId: string }> {
38
+ return await nextclawClient.sessions.delete(sessionId);
39
+ }
@@ -0,0 +1,50 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type {
3
+ RemoteAccessView,
4
+ RemoteAccountProfileUpdateRequest,
5
+ RemoteBrowserAuthPollRequest,
6
+ RemoteBrowserAuthPollResult,
7
+ RemoteBrowserAuthStartRequest,
8
+ RemoteBrowserAuthStartResult,
9
+ RemoteDoctorView,
10
+ RemoteLoginRequest,
11
+ RemoteServiceAction,
12
+ RemoteServiceActionResult,
13
+ RemoteSettingsUpdateRequest
14
+ } from '@/shared/lib/api/remote.types';
15
+
16
+ export async function fetchRemoteStatus(): Promise<RemoteAccessView> {
17
+ return await nextclawClient.remote.fetchStatus();
18
+ }
19
+
20
+ export async function fetchRemoteDoctor(): Promise<RemoteDoctorView> {
21
+ return await nextclawClient.remote.fetchDoctor();
22
+ }
23
+
24
+ export async function loginRemote(data: RemoteLoginRequest): Promise<RemoteAccessView> {
25
+ return await nextclawClient.remote.login(data);
26
+ }
27
+
28
+ export async function startRemoteBrowserAuth(data: RemoteBrowserAuthStartRequest): Promise<RemoteBrowserAuthStartResult> {
29
+ return await nextclawClient.remote.startBrowserAuth(data);
30
+ }
31
+
32
+ export async function pollRemoteBrowserAuth(data: RemoteBrowserAuthPollRequest): Promise<RemoteBrowserAuthPollResult> {
33
+ return await nextclawClient.remote.pollBrowserAuth(data);
34
+ }
35
+
36
+ export async function logoutRemote(): Promise<RemoteAccessView> {
37
+ return await nextclawClient.remote.logout();
38
+ }
39
+
40
+ export async function updateRemoteAccountProfile(data: RemoteAccountProfileUpdateRequest): Promise<RemoteAccessView> {
41
+ return await nextclawClient.remote.updateAccountProfile(data);
42
+ }
43
+
44
+ export async function updateRemoteSettings(data: RemoteSettingsUpdateRequest): Promise<RemoteAccessView> {
45
+ return await nextclawClient.remote.updateSettings(data);
46
+ }
47
+
48
+ export async function controlRemoteService(action: RemoteServiceAction): Promise<RemoteServiceActionResult> {
49
+ return await nextclawClient.remote.controlService(action);
50
+ }
@@ -0,0 +1,18 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type { RuntimeControlActionResult, RuntimeControlView } from '@/shared/lib/api/runtime-control.types';
3
+
4
+ export async function fetchRuntimeControl(): Promise<RuntimeControlView> {
5
+ return await nextclawClient.runtimeControl.fetch();
6
+ }
7
+
8
+ export async function startRuntimeService(): Promise<RuntimeControlActionResult> {
9
+ return await nextclawClient.runtimeControl.startService();
10
+ }
11
+
12
+ export async function restartRuntimeService(): Promise<RuntimeControlActionResult> {
13
+ return await nextclawClient.runtimeControl.restartService();
14
+ }
15
+
16
+ export async function stopRuntimeService(): Promise<RuntimeControlActionResult> {
17
+ return await nextclawClient.runtimeControl.stopService();
18
+ }
@@ -0,0 +1,26 @@
1
+ import type { UpdatePreferences, UpdateSnapshot } from '@nextclaw/kernel';
2
+ import { nextclawClient } from './client.service';
3
+
4
+ export async function fetchRuntimeUpdate(): Promise<UpdateSnapshot> {
5
+ return await nextclawClient.runtimeUpdate.fetch();
6
+ }
7
+
8
+ export async function checkRuntimeUpdate(): Promise<UpdateSnapshot> {
9
+ return await nextclawClient.runtimeUpdate.check();
10
+ }
11
+
12
+ export async function downloadRuntimeUpdate(): Promise<UpdateSnapshot> {
13
+ return await nextclawClient.runtimeUpdate.download();
14
+ }
15
+
16
+ export async function applyRuntimeUpdate(): Promise<UpdateSnapshot> {
17
+ return await nextclawClient.runtimeUpdate.apply();
18
+ }
19
+
20
+ export async function updateRuntimeUpdatePreferences(preferences: Partial<UpdatePreferences>): Promise<UpdateSnapshot> {
21
+ return await nextclawClient.runtimeUpdate.updatePreferences(preferences);
22
+ }
23
+
24
+ export async function updateRuntimeUpdateChannel(channel: UpdateSnapshot['channel']): Promise<UpdateSnapshot> {
25
+ return await nextclawClient.runtimeUpdate.updateChannel(channel);
26
+ }
@@ -0,0 +1,16 @@
1
+ import { nextclawClient } from './client.service';
2
+ import type { ServerPathBrowseView, ServerPathReadView } from '@/shared/lib/api/types';
3
+
4
+ export async function fetchServerPathBrowse(params?: {
5
+ path?: string | null;
6
+ includeFiles?: boolean;
7
+ }): Promise<ServerPathBrowseView> {
8
+ return await nextclawClient.serverPaths.browse(params);
9
+ }
10
+
11
+ export async function fetchServerPathRead(params: {
12
+ path: string;
13
+ basePath?: string | null;
14
+ }): Promise<ServerPathReadView> {
15
+ return await nextclawClient.serverPaths.read(params);
16
+ }
@@ -1,4 +1,5 @@
1
1
  export { appClient } from './app-client.service';
2
+ export { requestRawApiResponse } from './request-raw-api-response.utils';
2
3
  export type {
3
4
  AppEvent,
4
5
  AppTransport,
@@ -1,5 +1,6 @@
1
1
  import { systemStatusManager } from '@/features/system-status';
2
2
  import type { AppEvent, AppTransport, RequestInput, StreamInput, StreamSession } from './transport.types';
3
+ import { requestRawApiResponse } from './request-raw-api-response.utils';
3
4
  import { readSseStreamResult } from './sse-stream.utils';
4
5
  import { resolveTransportWebSocketUrl } from './transport-websocket-url.utils';
5
6
 
@@ -139,13 +140,14 @@ export class LocalAppTransport implements AppTransport {
139
140
  const timeoutId = timeoutMs
140
141
  ? window.setTimeout(() => controller?.abort(`Request timed out after ${timeoutMs}ms: ${input.method} ${input.path}`), timeoutMs)
141
142
  : null;
143
+ const requestBody = normalizeRequestBody(input.body);
142
144
 
143
145
  try {
144
- const { requestRawApiResponse } = await import('@/shared/lib/api');
145
- const response = await requestRawApiResponse<T>(input.path, {
146
+ const response = await requestRawApiResponse<T>(this.apiBase, input.path, {
146
147
  method: input.method,
147
- ...(input.body !== undefined ? { body: JSON.stringify(input.body) } : {}),
148
- ...(controller ? { signal: controller.signal } : {})
148
+ ...(requestBody !== undefined ? { body: requestBody } : {}),
149
+ ...(input.headers ? { headers: input.headers } : {}),
150
+ signal: controller?.signal ?? input.signal
149
151
  });
150
152
  if (!response.ok) {
151
153
  throw createTransportError(response, `Request failed for ${input.method} ${input.path}`);
@@ -228,3 +230,21 @@ export class LocalAppTransport implements AppTransport {
228
230
  return this.realtimeGateway.subscribe(handler);
229
231
  };
230
232
  }
233
+
234
+ function normalizeRequestBody(body: unknown): BodyInit | undefined {
235
+ if (body === undefined) {
236
+ return undefined;
237
+ }
238
+ if (
239
+ body instanceof FormData ||
240
+ body instanceof URLSearchParams ||
241
+ body instanceof Blob ||
242
+ body instanceof ArrayBuffer
243
+ ) {
244
+ return body;
245
+ }
246
+ if (typeof body === 'string') {
247
+ return body;
248
+ }
249
+ return JSON.stringify(body);
250
+ }
@@ -1,4 +1,4 @@
1
- import type { ApiError } from '@/shared/lib/api';
1
+ import type { ApiError } from '@nextclaw/server';
2
2
  import type { AppEvent, AppTransport, RemoteRuntimeInfo, RequestInput, StreamInput, StreamSession } from './transport.types';
3
3
  import { resolveTransportWebSocketUrl } from './transport-websocket-url.utils';
4
4
 
@@ -0,0 +1,133 @@
1
+ import type { ApiResponse } from '@nextclaw/server';
2
+ import { systemStatusManager } from '@/features/system-status';
3
+
4
+ function compactSnippet(text: string) {
5
+ return text.replace(/\s+/g, ' ').trim().slice(0, 200);
6
+ }
7
+
8
+ function inferNonJsonHint(endpoint: string, status: number): string | undefined {
9
+ if (
10
+ status === 404 &&
11
+ endpoint.startsWith('/api/config/providers/') &&
12
+ endpoint.endsWith('/test')
13
+ ) {
14
+ return 'Provider test endpoint is missing. This usually means nextclaw runtime version is outdated.';
15
+ }
16
+ if (status === 401 || status === 403) {
17
+ return 'Authentication failed. Check apiKey and custom headers.';
18
+ }
19
+ if (status === 429) {
20
+ return 'Rate limited by upstream provider. Retry later or switch model/provider.';
21
+ }
22
+ if (status >= 500) {
23
+ return 'Upstream service error. Retry later and inspect server logs if it persists.';
24
+ }
25
+ return undefined;
26
+ }
27
+
28
+ function formatUnknownFetchError(error: unknown): {
29
+ summary: string;
30
+ details: Record<string, unknown>;
31
+ } {
32
+ if (error instanceof Error) {
33
+ const name = error.name?.trim() || 'Error';
34
+ const message = error.message?.trim() || 'Unknown error';
35
+ return {
36
+ summary: `${name}: ${message}`,
37
+ details: {
38
+ errorName: name,
39
+ errorMessage: message,
40
+ ...(error.stack?.trim() ? { errorStack: error.stack.trim() } : {})
41
+ }
42
+ };
43
+ }
44
+ return {
45
+ summary: String(error ?? 'Unknown error'),
46
+ details: {
47
+ errorName: 'NonError',
48
+ errorMessage: String(error ?? 'Unknown error')
49
+ }
50
+ };
51
+ }
52
+
53
+ export async function requestRawApiResponse<T>(
54
+ apiBase: string,
55
+ endpoint: string,
56
+ options: RequestInit = {}
57
+ ): Promise<ApiResponse<T>> {
58
+ const url = `${apiBase}${endpoint}`;
59
+ const method = (options.method || 'GET').toUpperCase();
60
+
61
+ let response: Response;
62
+ try {
63
+ const headers = new Headers(options.headers);
64
+ if (!(options.body instanceof FormData) && !headers.has('Content-Type')) {
65
+ headers.set('Content-Type', 'application/json');
66
+ }
67
+ response = await fetch(url, {
68
+ credentials: 'include',
69
+ headers,
70
+ ...options
71
+ });
72
+ } catch (error) {
73
+ const formatted = formatUnknownFetchError(error);
74
+ systemStatusManager.reportTransportFailure(formatted.summary);
75
+ return {
76
+ ok: false,
77
+ error: {
78
+ code: 'NETWORK_ERROR',
79
+ message: `Fetch failed on ${method} ${endpoint} | ${formatted.summary}`,
80
+ details: {
81
+ method,
82
+ endpoint,
83
+ url,
84
+ ...formatted.details
85
+ }
86
+ }
87
+ };
88
+ }
89
+
90
+ const text = await response.text();
91
+ let data: ApiResponse<T> | null = null;
92
+ if (text) {
93
+ try {
94
+ data = JSON.parse(text) as ApiResponse<T>;
95
+ } catch {
96
+ // fall through to build a synthetic error response
97
+ }
98
+ }
99
+
100
+ if (!data) {
101
+ const snippet = text ? compactSnippet(text) : '';
102
+ const hint = inferNonJsonHint(endpoint, response.status);
103
+ const parts = [`Non-JSON response (${response.status} ${response.statusText}) on ${method} ${endpoint}`];
104
+ if (snippet) {
105
+ parts.push(`body=${snippet}`);
106
+ }
107
+ if (hint) {
108
+ parts.push(`hint=${hint}`);
109
+ }
110
+ return {
111
+ ok: false,
112
+ error: {
113
+ code: 'INVALID_RESPONSE',
114
+ message: parts.join(' | '),
115
+ details: {
116
+ status: response.status,
117
+ statusText: response.statusText,
118
+ method,
119
+ endpoint,
120
+ url,
121
+ bodySnippet: snippet || undefined,
122
+ hint
123
+ }
124
+ }
125
+ };
126
+ }
127
+
128
+ if (!response.ok) {
129
+ return data as ApiResponse<T>;
130
+ }
131
+
132
+ return data as ApiResponse<T>;
133
+ }