@nextclaw/server 0.11.0 → 0.11.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.
- package/dist/index.d.ts +32 -119
- package/dist/index.js +42 -702
- package/package.json +6 -6
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as NextclawCore from '@nextclaw/core';
|
|
2
2
|
import { ThinkingLevel, CronService, Config, ConfigActionExecuteRequest as ConfigActionExecuteRequest$1, ConfigActionExecuteResult as ConfigActionExecuteResult$1 } from '@nextclaw/core';
|
|
3
3
|
import { PluginChannelBinding, PluginUiMetadata } from '@nextclaw/openclaw-compat';
|
|
4
|
-
import { NcpAgentClientEndpoint,
|
|
4
|
+
import { NcpAgentClientEndpoint, NcpSessionSummary, NcpMessage, NcpSessionApi } from '@nextclaw/ncp';
|
|
5
5
|
import { NcpHttpAgentStreamProvider } from '@nextclaw/ncp-http-agent-server';
|
|
6
6
|
import { IncomingMessage } from 'node:http';
|
|
7
7
|
import { Hono } from 'hono';
|
|
@@ -353,10 +353,11 @@ type UiRouterOptions = {
|
|
|
353
353
|
applyLiveConfigReload?: () => Promise<void>;
|
|
354
354
|
marketplace?: MarketplaceApiConfig;
|
|
355
355
|
cronService?: InstanceType<typeof NextclawCore.CronService>;
|
|
356
|
-
chatRuntime?: UiChatRuntime;
|
|
357
356
|
ncpAgent?: UiNcpAgent;
|
|
357
|
+
ncpSessionService?: UiNcpSessionService;
|
|
358
358
|
authService?: UiAuthService;
|
|
359
359
|
remoteAccess?: UiRemoteAccessHost;
|
|
360
|
+
getBootstrapStatus?: () => BootstrapStatusView;
|
|
360
361
|
getPluginChannelBindings?: () => PluginChannelBinding[];
|
|
361
362
|
getPluginUiMetadata?: () => PluginUiMetadata[];
|
|
362
363
|
};
|
|
@@ -407,6 +408,31 @@ type AppMetaView = {
|
|
|
407
408
|
name: string;
|
|
408
409
|
productVersion: string;
|
|
409
410
|
};
|
|
411
|
+
type BootstrapPhase = "kernel-starting" | "shell-ready" | "hydrating-capabilities" | "ready" | "error";
|
|
412
|
+
type BootstrapStageState = "pending" | "running" | "ready" | "error";
|
|
413
|
+
type BootstrapRemoteState = "pending" | "ready" | "conflict" | "disabled" | "error";
|
|
414
|
+
type BootstrapStatusView = {
|
|
415
|
+
phase: BootstrapPhase;
|
|
416
|
+
shellReadyAt?: string;
|
|
417
|
+
pluginHydration: {
|
|
418
|
+
state: BootstrapStageState;
|
|
419
|
+
loadedPluginCount: number;
|
|
420
|
+
totalPluginCount: number;
|
|
421
|
+
startedAt?: string;
|
|
422
|
+
completedAt?: string;
|
|
423
|
+
error?: string;
|
|
424
|
+
};
|
|
425
|
+
channels: {
|
|
426
|
+
state: BootstrapStageState;
|
|
427
|
+
enabled: string[];
|
|
428
|
+
error?: string;
|
|
429
|
+
};
|
|
430
|
+
remote: {
|
|
431
|
+
state: BootstrapRemoteState;
|
|
432
|
+
message?: string;
|
|
433
|
+
};
|
|
434
|
+
lastError?: string;
|
|
435
|
+
};
|
|
410
436
|
type ProviderConfigView = {
|
|
411
437
|
enabled: boolean;
|
|
412
438
|
displayName?: string;
|
|
@@ -840,115 +866,6 @@ type SecretsConfigUpdate = {
|
|
|
840
866
|
providers?: Record<string, SecretProviderView> | null;
|
|
841
867
|
refs?: Record<string, SecretRefView> | null;
|
|
842
868
|
};
|
|
843
|
-
type ChatTurnRequest = {
|
|
844
|
-
message: string;
|
|
845
|
-
sessionKey?: string;
|
|
846
|
-
agentId?: string;
|
|
847
|
-
channel?: string;
|
|
848
|
-
chatId?: string;
|
|
849
|
-
model?: string;
|
|
850
|
-
metadata?: Record<string, unknown>;
|
|
851
|
-
runId?: string;
|
|
852
|
-
};
|
|
853
|
-
type ChatTurnResult = {
|
|
854
|
-
reply: string;
|
|
855
|
-
sessionKey: string;
|
|
856
|
-
agentId?: string;
|
|
857
|
-
model?: string;
|
|
858
|
-
metadata?: Record<string, unknown>;
|
|
859
|
-
};
|
|
860
|
-
type ChatTurnStreamEvent = {
|
|
861
|
-
type: "delta";
|
|
862
|
-
delta: string;
|
|
863
|
-
} | {
|
|
864
|
-
type: "session_event";
|
|
865
|
-
event: SessionEventView;
|
|
866
|
-
} | {
|
|
867
|
-
type: "final";
|
|
868
|
-
result: ChatTurnResult;
|
|
869
|
-
} | {
|
|
870
|
-
type: "error";
|
|
871
|
-
error: string;
|
|
872
|
-
};
|
|
873
|
-
type ChatTurnView = {
|
|
874
|
-
reply: string;
|
|
875
|
-
sessionKey: string;
|
|
876
|
-
agentId?: string;
|
|
877
|
-
model?: string;
|
|
878
|
-
requestedAt: string;
|
|
879
|
-
completedAt: string;
|
|
880
|
-
durationMs: number;
|
|
881
|
-
};
|
|
882
|
-
type ChatCapabilitiesView = {
|
|
883
|
-
stopSupported: boolean;
|
|
884
|
-
stopReason?: string;
|
|
885
|
-
};
|
|
886
|
-
type ChatCommandOptionView = {
|
|
887
|
-
name: string;
|
|
888
|
-
description: string;
|
|
889
|
-
type: "string" | "boolean" | "number";
|
|
890
|
-
required?: boolean;
|
|
891
|
-
};
|
|
892
|
-
type ChatCommandView = {
|
|
893
|
-
name: string;
|
|
894
|
-
description: string;
|
|
895
|
-
options?: ChatCommandOptionView[];
|
|
896
|
-
};
|
|
897
|
-
type ChatCommandsView = {
|
|
898
|
-
commands: ChatCommandView[];
|
|
899
|
-
total: number;
|
|
900
|
-
};
|
|
901
|
-
type ChatTurnStopRequest = {
|
|
902
|
-
runId: string;
|
|
903
|
-
sessionKey?: string;
|
|
904
|
-
agentId?: string;
|
|
905
|
-
};
|
|
906
|
-
type ChatTurnStopResult = {
|
|
907
|
-
stopped: boolean;
|
|
908
|
-
runId: string;
|
|
909
|
-
sessionKey?: string;
|
|
910
|
-
reason?: string;
|
|
911
|
-
};
|
|
912
|
-
type ChatRunState = "queued" | "running" | "completed" | "failed" | "aborted";
|
|
913
|
-
type ChatRunView = {
|
|
914
|
-
runId: string;
|
|
915
|
-
sessionKey: string;
|
|
916
|
-
agentId?: string;
|
|
917
|
-
model?: string;
|
|
918
|
-
state: ChatRunState;
|
|
919
|
-
requestedAt: string;
|
|
920
|
-
startedAt?: string;
|
|
921
|
-
completedAt?: string;
|
|
922
|
-
stopSupported: boolean;
|
|
923
|
-
stopReason?: string;
|
|
924
|
-
error?: string;
|
|
925
|
-
reply?: string;
|
|
926
|
-
eventCount: number;
|
|
927
|
-
};
|
|
928
|
-
type ChatRunListView = {
|
|
929
|
-
runs: ChatRunView[];
|
|
930
|
-
total: number;
|
|
931
|
-
};
|
|
932
|
-
type UiChatRuntime = {
|
|
933
|
-
processTurn: (params: ChatTurnRequest) => Promise<ChatTurnResult>;
|
|
934
|
-
processTurnStream?: (params: ChatTurnRequest) => AsyncGenerator<ChatTurnStreamEvent>;
|
|
935
|
-
startTurnRun?: (params: ChatTurnRequest) => Promise<ChatRunView> | ChatRunView;
|
|
936
|
-
streamRun?: (params: {
|
|
937
|
-
runId: string;
|
|
938
|
-
fromEventIndex?: number;
|
|
939
|
-
}) => AsyncGenerator<ChatTurnStreamEvent>;
|
|
940
|
-
getRun?: (params: {
|
|
941
|
-
runId: string;
|
|
942
|
-
}) => Promise<ChatRunView | null> | ChatRunView | null;
|
|
943
|
-
listRuns?: (params: {
|
|
944
|
-
sessionKey?: string;
|
|
945
|
-
states?: ChatRunState[];
|
|
946
|
-
limit?: number;
|
|
947
|
-
}) => Promise<ChatRunListView> | ChatRunListView;
|
|
948
|
-
getCapabilities?: (params: Pick<ChatTurnRequest, "sessionKey" | "agentId">) => Promise<ChatCapabilitiesView> | ChatCapabilitiesView;
|
|
949
|
-
listSessionTypes?: () => Promise<ChatSessionTypesView> | ChatSessionTypesView;
|
|
950
|
-
stopTurn?: (params: ChatTurnStopRequest) => Promise<ChatTurnStopResult> | ChatTurnStopResult;
|
|
951
|
-
};
|
|
952
869
|
type UiNcpSessionListView = {
|
|
953
870
|
sessions: NcpSessionSummary[];
|
|
954
871
|
total: number;
|
|
@@ -961,10 +878,10 @@ type UiNcpSessionMessagesView = {
|
|
|
961
878
|
type SessionTypeDescribeParams = {
|
|
962
879
|
describeMode?: "observation" | "probe";
|
|
963
880
|
};
|
|
881
|
+
type UiNcpSessionService = NcpSessionApi;
|
|
964
882
|
type UiNcpAgent = {
|
|
965
883
|
agentClientEndpoint: NcpAgentClientEndpoint;
|
|
966
884
|
streamProvider?: NcpHttpAgentStreamProvider;
|
|
967
|
-
sessionApi?: NcpSessionApi;
|
|
968
885
|
listSessionTypes?: (params?: SessionTypeDescribeParams) => Promise<ChatSessionTypesView> | ChatSessionTypesView;
|
|
969
886
|
assetApi?: {
|
|
970
887
|
put: (input: {
|
|
@@ -1148,11 +1065,6 @@ type UiServerEvent = {
|
|
|
1148
1065
|
payload: {
|
|
1149
1066
|
path: string;
|
|
1150
1067
|
};
|
|
1151
|
-
} | {
|
|
1152
|
-
type: "run.updated";
|
|
1153
|
-
payload: {
|
|
1154
|
-
run: ChatRunView;
|
|
1155
|
-
};
|
|
1156
1068
|
} | {
|
|
1157
1069
|
type: "session.updated";
|
|
1158
1070
|
payload: {
|
|
@@ -1180,9 +1092,10 @@ type UiServerOptions = {
|
|
|
1180
1092
|
staticDir?: string;
|
|
1181
1093
|
marketplace?: MarketplaceApiConfig;
|
|
1182
1094
|
cronService?: CronService;
|
|
1183
|
-
chatRuntime?: UiChatRuntime;
|
|
1184
1095
|
ncpAgent?: UiNcpAgent;
|
|
1096
|
+
ncpSessionService?: UiNcpSessionService;
|
|
1185
1097
|
remoteAccess?: UiRemoteAccessHost;
|
|
1098
|
+
getBootstrapStatus?: () => BootstrapStatusView;
|
|
1186
1099
|
getPluginChannelBindings?: () => PluginChannelBinding[];
|
|
1187
1100
|
getPluginUiMetadata?: () => PluginUiMetadata[];
|
|
1188
1101
|
};
|
|
@@ -1254,4 +1167,4 @@ declare function getUiBridgeSecretPath(): string;
|
|
|
1254
1167
|
declare function readUiBridgeSecret(): string | null;
|
|
1255
1168
|
declare function ensureUiBridgeSecret(): string;
|
|
1256
1169
|
|
|
1257
|
-
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type AuthEnabledUpdateRequest, type AuthLoginRequest, type AuthPasswordUpdateRequest, type AuthSetupRequest, type AuthStatusView, type BindingPeerView, type BochaFreshnessValue, type
|
|
1170
|
+
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type AppMetaView, type AuthEnabledUpdateRequest, type AuthLoginRequest, type AuthPasswordUpdateRequest, type AuthSetupRequest, type AuthStatusView, type BindingPeerView, type BochaFreshnessValue, type BootstrapPhase, type BootstrapRemoteState, type BootstrapStageState, type BootstrapStatusView, type ChannelAuthPollRequest, type ChannelAuthPollResult, type ChannelAuthStartRequest, type ChannelAuthStartResult, type ChannelSpecView, type ChatSessionTypeCtaView, type ChatSessionTypeOptionView, type ChatSessionTypesView, type ConfigActionExecuteRequest, type ConfigActionExecuteResult, type ConfigActionManifest, type ConfigActionType, type ConfigMetaView, type ConfigSchemaResponse, type ConfigUiHint, type ConfigUiHints, type ConfigView, type CronActionResult, type CronEnableRequest, type CronJobStateView, type CronJobView, type CronListView, type CronPayloadView, type CronRunRequest, type CronScheduleView, DEFAULT_SESSION_TYPE, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplaceLocalizedTextMap, type MarketplaceMcpContentView, type MarketplaceMcpDoctorResult, type MarketplaceMcpInstallKind, type MarketplaceMcpInstallRequest, type MarketplaceMcpInstallResult, type MarketplaceMcpInstallSpec, type MarketplaceMcpManageAction, type MarketplaceMcpManageRequest, type MarketplaceMcpManageResult, type MarketplaceMcpTemplateInput, type MarketplacePluginContentView, type MarketplacePluginInstallKind, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallKind, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderAuthImportResult, type ProviderAuthPollRequest, type ProviderAuthPollResult, type ProviderAuthStartRequest, type ProviderAuthStartResult, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderCreateRequest, type ProviderCreateResult, type ProviderDeleteResult, type ProviderSpecView, type RemoteAccessView, type RemoteAccountView, type RemoteBrowserAuthPollRequest, type RemoteBrowserAuthPollResult, type RemoteBrowserAuthStartRequest, type RemoteBrowserAuthStartResult, type RemoteDoctorCheckView, type RemoteDoctorView, type RemoteLoginRequest, type RemoteRuntimeView, type RemoteServiceAction, type RemoteServiceActionResult, type RemoteServiceView, type RemoteSettingsUpdateRequest, type RemoteSettingsView, type RuntimeConfigUpdate, type SearchConfigUpdate, type SearchConfigView, type SearchProviderConfigView, type SearchProviderName, type SearchProviderSpecView, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, SessionPatchValidationError, type SessionTypeDescribeParams, type SessionsListView, type UiNcpAgent, type UiNcpAssetPutView, type UiNcpAssetView, type UiNcpSessionListView, type UiNcpSessionMessagesView, type UiNcpSessionService, type UiNcpStoredAssetRecord, type UiRemoteAccessHost, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createCustomProvider, createUiRouter, deleteCustomProvider, deleteSession, ensureUiBridgeSecret, executeConfigAction, getSessionHistory, getUiBridgeSecretPath, listSessions, loadConfigOrDefault, patchSession, readUiBridgeSecret, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSearch, updateSecrets };
|
package/dist/index.js
CHANGED
|
@@ -388,6 +388,23 @@ function buildAppMetaView(options) {
|
|
|
388
388
|
productVersion: productVersion && productVersion.length > 0 ? productVersion : "0.0.0"
|
|
389
389
|
};
|
|
390
390
|
}
|
|
391
|
+
function buildFallbackBootstrapStatus() {
|
|
392
|
+
return {
|
|
393
|
+
phase: "kernel-starting",
|
|
394
|
+
pluginHydration: {
|
|
395
|
+
state: "pending",
|
|
396
|
+
loadedPluginCount: 0,
|
|
397
|
+
totalPluginCount: 0
|
|
398
|
+
},
|
|
399
|
+
channels: {
|
|
400
|
+
state: "pending",
|
|
401
|
+
enabled: []
|
|
402
|
+
},
|
|
403
|
+
remote: {
|
|
404
|
+
state: "pending"
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
}
|
|
391
408
|
var AppRoutesController = class {
|
|
392
409
|
constructor(options) {
|
|
393
410
|
this.options = options;
|
|
@@ -396,12 +413,13 @@ var AppRoutesController = class {
|
|
|
396
413
|
ok({
|
|
397
414
|
status: "ok",
|
|
398
415
|
services: {
|
|
399
|
-
|
|
416
|
+
ncpAgent: this.options.ncpAgent ? "ready" : "unavailable",
|
|
400
417
|
cronService: this.options.cronService ? "ready" : "unavailable"
|
|
401
418
|
}
|
|
402
419
|
})
|
|
403
420
|
);
|
|
404
421
|
appMeta = (c) => c.json(ok(buildAppMetaView(this.options)));
|
|
422
|
+
bootstrapStatus = (c) => c.json(ok(this.options.getBootstrapStatus?.() ?? buildFallbackBootstrapStatus()));
|
|
405
423
|
};
|
|
406
424
|
|
|
407
425
|
// src/ui/auth-bridge.ts
|
|
@@ -548,9 +566,6 @@ var AuthRoutesController = class {
|
|
|
548
566
|
};
|
|
549
567
|
};
|
|
550
568
|
|
|
551
|
-
// src/ui/router/chat.controller.ts
|
|
552
|
-
import * as NextclawCore2 from "@nextclaw/core";
|
|
553
|
-
|
|
554
569
|
// src/ui/config.ts
|
|
555
570
|
import {
|
|
556
571
|
loadConfig as loadConfig2,
|
|
@@ -2017,596 +2032,6 @@ function updateSecrets(configPath, patch) {
|
|
|
2017
2032
|
};
|
|
2018
2033
|
}
|
|
2019
2034
|
|
|
2020
|
-
// src/ui/router/chat-utils.ts
|
|
2021
|
-
import * as NextclawCore from "@nextclaw/core";
|
|
2022
|
-
function normalizeSessionType2(value) {
|
|
2023
|
-
return readNonEmptyString(value)?.toLowerCase();
|
|
2024
|
-
}
|
|
2025
|
-
function resolveSessionTypeLabel(sessionType) {
|
|
2026
|
-
if (sessionType === "native") {
|
|
2027
|
-
return "Native";
|
|
2028
|
-
}
|
|
2029
|
-
return sessionType.trim().split(/[-_]+/g).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ") || sessionType;
|
|
2030
|
-
}
|
|
2031
|
-
async function buildChatSessionTypesView(chatRuntime) {
|
|
2032
|
-
if (!chatRuntime?.listSessionTypes) {
|
|
2033
|
-
return {
|
|
2034
|
-
defaultType: DEFAULT_SESSION_TYPE,
|
|
2035
|
-
options: [{ value: DEFAULT_SESSION_TYPE, label: resolveSessionTypeLabel(DEFAULT_SESSION_TYPE) }]
|
|
2036
|
-
};
|
|
2037
|
-
}
|
|
2038
|
-
const payload = await chatRuntime.listSessionTypes();
|
|
2039
|
-
const deduped = /* @__PURE__ */ new Map();
|
|
2040
|
-
for (const rawOption of payload.options ?? []) {
|
|
2041
|
-
const normalized = normalizeSessionType2(rawOption.value);
|
|
2042
|
-
if (!normalized) {
|
|
2043
|
-
continue;
|
|
2044
|
-
}
|
|
2045
|
-
deduped.set(normalized, {
|
|
2046
|
-
value: normalized,
|
|
2047
|
-
label: readNonEmptyString(rawOption.label) ?? resolveSessionTypeLabel(normalized)
|
|
2048
|
-
});
|
|
2049
|
-
}
|
|
2050
|
-
if (!deduped.has(DEFAULT_SESSION_TYPE)) {
|
|
2051
|
-
deduped.set(DEFAULT_SESSION_TYPE, {
|
|
2052
|
-
value: DEFAULT_SESSION_TYPE,
|
|
2053
|
-
label: resolveSessionTypeLabel(DEFAULT_SESSION_TYPE)
|
|
2054
|
-
});
|
|
2055
|
-
}
|
|
2056
|
-
const defaultType = normalizeSessionType2(payload.defaultType) ?? DEFAULT_SESSION_TYPE;
|
|
2057
|
-
if (!deduped.has(defaultType)) {
|
|
2058
|
-
deduped.set(defaultType, {
|
|
2059
|
-
value: defaultType,
|
|
2060
|
-
label: resolveSessionTypeLabel(defaultType)
|
|
2061
|
-
});
|
|
2062
|
-
}
|
|
2063
|
-
const options = Array.from(deduped.values()).sort((left, right) => {
|
|
2064
|
-
if (left.value === DEFAULT_SESSION_TYPE) {
|
|
2065
|
-
return -1;
|
|
2066
|
-
}
|
|
2067
|
-
if (right.value === DEFAULT_SESSION_TYPE) {
|
|
2068
|
-
return 1;
|
|
2069
|
-
}
|
|
2070
|
-
return left.value.localeCompare(right.value);
|
|
2071
|
-
});
|
|
2072
|
-
return {
|
|
2073
|
-
defaultType,
|
|
2074
|
-
options
|
|
2075
|
-
};
|
|
2076
|
-
}
|
|
2077
|
-
function resolveAgentIdFromSessionKey(sessionKey) {
|
|
2078
|
-
const parsed = NextclawCore.parseAgentScopedSessionKey(sessionKey);
|
|
2079
|
-
const agentId = readNonEmptyString(parsed?.agentId);
|
|
2080
|
-
return agentId;
|
|
2081
|
-
}
|
|
2082
|
-
function createChatRunId() {
|
|
2083
|
-
const now = Date.now().toString(36);
|
|
2084
|
-
const rand = Math.random().toString(36).slice(2, 10);
|
|
2085
|
-
return `run-${now}-${rand}`;
|
|
2086
|
-
}
|
|
2087
|
-
function isChatRunState(value) {
|
|
2088
|
-
return value === "queued" || value === "running" || value === "completed" || value === "failed" || value === "aborted";
|
|
2089
|
-
}
|
|
2090
|
-
function readChatRunStates(value) {
|
|
2091
|
-
if (typeof value !== "string") {
|
|
2092
|
-
return void 0;
|
|
2093
|
-
}
|
|
2094
|
-
const values = value.split(",").map((item) => item.trim().toLowerCase()).filter((item) => Boolean(item) && isChatRunState(item));
|
|
2095
|
-
if (values.length === 0) {
|
|
2096
|
-
return void 0;
|
|
2097
|
-
}
|
|
2098
|
-
return Array.from(new Set(values));
|
|
2099
|
-
}
|
|
2100
|
-
function buildChatTurnView(params) {
|
|
2101
|
-
const completedAt = /* @__PURE__ */ new Date();
|
|
2102
|
-
return {
|
|
2103
|
-
reply: String(params.result.reply ?? ""),
|
|
2104
|
-
sessionKey: readNonEmptyString(params.result.sessionKey) ?? params.fallbackSessionKey,
|
|
2105
|
-
...readNonEmptyString(params.result.agentId) || params.requestedAgentId ? { agentId: readNonEmptyString(params.result.agentId) ?? params.requestedAgentId } : {},
|
|
2106
|
-
...readNonEmptyString(params.result.model) || params.requestedModel ? { model: readNonEmptyString(params.result.model) ?? params.requestedModel } : {},
|
|
2107
|
-
requestedAt: params.requestedAt.toISOString(),
|
|
2108
|
-
completedAt: completedAt.toISOString(),
|
|
2109
|
-
durationMs: Math.max(0, completedAt.getTime() - params.startedAtMs)
|
|
2110
|
-
};
|
|
2111
|
-
}
|
|
2112
|
-
function buildChatTurnViewFromRun(params) {
|
|
2113
|
-
const requestedAt = readNonEmptyString(params.run.requestedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2114
|
-
const completedAt = readNonEmptyString(params.run.completedAt) ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
2115
|
-
const requestedAtMs = Date.parse(requestedAt);
|
|
2116
|
-
const completedAtMs = Date.parse(completedAt);
|
|
2117
|
-
return {
|
|
2118
|
-
reply: readNonEmptyString(params.run.reply) ?? params.fallbackReply ?? "",
|
|
2119
|
-
sessionKey: readNonEmptyString(params.run.sessionKey) ?? params.fallbackSessionKey,
|
|
2120
|
-
...readNonEmptyString(params.run.agentId) || params.fallbackAgentId ? { agentId: readNonEmptyString(params.run.agentId) ?? params.fallbackAgentId } : {},
|
|
2121
|
-
...readNonEmptyString(params.run.model) || params.fallbackModel ? { model: readNonEmptyString(params.run.model) ?? params.fallbackModel } : {},
|
|
2122
|
-
requestedAt,
|
|
2123
|
-
completedAt,
|
|
2124
|
-
durationMs: Number.isFinite(requestedAtMs) && Number.isFinite(completedAtMs) ? Math.max(0, completedAtMs - requestedAtMs) : 0
|
|
2125
|
-
};
|
|
2126
|
-
}
|
|
2127
|
-
function toSseFrame(event, data) {
|
|
2128
|
-
return `event: ${event}
|
|
2129
|
-
data: ${JSON.stringify(data)}
|
|
2130
|
-
|
|
2131
|
-
`;
|
|
2132
|
-
}
|
|
2133
|
-
|
|
2134
|
-
// src/ui/router/chat.controller.ts
|
|
2135
|
-
var ChatRoutesController = class {
|
|
2136
|
-
constructor(options) {
|
|
2137
|
-
this.options = options;
|
|
2138
|
-
}
|
|
2139
|
-
getCapabilities = async (c) => {
|
|
2140
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2141
|
-
if (!chatRuntime) {
|
|
2142
|
-
return c.json(err("NOT_AVAILABLE", "chat runtime unavailable"), 503);
|
|
2143
|
-
}
|
|
2144
|
-
const query = c.req.query();
|
|
2145
|
-
const params = {
|
|
2146
|
-
sessionKey: readNonEmptyString(query.sessionKey),
|
|
2147
|
-
agentId: readNonEmptyString(query.agentId)
|
|
2148
|
-
};
|
|
2149
|
-
try {
|
|
2150
|
-
const capabilities = chatRuntime.getCapabilities ? await chatRuntime.getCapabilities(params) : { stopSupported: Boolean(chatRuntime.stopTurn) };
|
|
2151
|
-
return c.json(ok(capabilities));
|
|
2152
|
-
} catch (error) {
|
|
2153
|
-
return c.json(err("CHAT_RUNTIME_FAILED", String(error)), 500);
|
|
2154
|
-
}
|
|
2155
|
-
};
|
|
2156
|
-
getSessionTypes = async (c) => {
|
|
2157
|
-
try {
|
|
2158
|
-
const payload = await buildChatSessionTypesView(this.options.chatRuntime);
|
|
2159
|
-
return c.json(ok(payload));
|
|
2160
|
-
} catch (error) {
|
|
2161
|
-
return c.json(err("CHAT_SESSION_TYPES_FAILED", String(error)), 500);
|
|
2162
|
-
}
|
|
2163
|
-
};
|
|
2164
|
-
getCommands = async (c) => {
|
|
2165
|
-
try {
|
|
2166
|
-
const config = loadConfigOrDefault(this.options.configPath);
|
|
2167
|
-
const registry = new NextclawCore2.CommandRegistry(config);
|
|
2168
|
-
const commands = registry.listSlashCommands().map((command) => ({
|
|
2169
|
-
name: command.name,
|
|
2170
|
-
description: command.description,
|
|
2171
|
-
...Array.isArray(command.options) && command.options.length > 0 ? {
|
|
2172
|
-
options: command.options.map((option) => ({
|
|
2173
|
-
name: option.name,
|
|
2174
|
-
description: option.description,
|
|
2175
|
-
type: option.type,
|
|
2176
|
-
...option.required === true ? { required: true } : {}
|
|
2177
|
-
}))
|
|
2178
|
-
} : {}
|
|
2179
|
-
}));
|
|
2180
|
-
const payload = {
|
|
2181
|
-
commands,
|
|
2182
|
-
total: commands.length
|
|
2183
|
-
};
|
|
2184
|
-
return c.json(ok(payload));
|
|
2185
|
-
} catch (error) {
|
|
2186
|
-
return c.json(err("CHAT_COMMANDS_FAILED", String(error)), 500);
|
|
2187
|
-
}
|
|
2188
|
-
};
|
|
2189
|
-
processTurn = async (c) => {
|
|
2190
|
-
if (!this.options.chatRuntime) {
|
|
2191
|
-
return c.json(err("NOT_AVAILABLE", "chat runtime unavailable"), 503);
|
|
2192
|
-
}
|
|
2193
|
-
const body = await readJson(c.req.raw);
|
|
2194
|
-
if (!body.ok) {
|
|
2195
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
2196
|
-
}
|
|
2197
|
-
const message = readNonEmptyString(body.data.message);
|
|
2198
|
-
if (!message) {
|
|
2199
|
-
return c.json(err("INVALID_BODY", "message is required"), 400);
|
|
2200
|
-
}
|
|
2201
|
-
const sessionKey = readNonEmptyString(body.data.sessionKey) ?? `ui:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
2202
|
-
const requestedAt = /* @__PURE__ */ new Date();
|
|
2203
|
-
const startedAtMs = requestedAt.getTime();
|
|
2204
|
-
const metadata = isRecord(body.data.metadata) ? body.data.metadata : void 0;
|
|
2205
|
-
const requestedAgentId = readNonEmptyString(body.data.agentId) ?? resolveAgentIdFromSessionKey(sessionKey);
|
|
2206
|
-
const requestedModel = readNonEmptyString(body.data.model);
|
|
2207
|
-
const request = {
|
|
2208
|
-
message,
|
|
2209
|
-
sessionKey,
|
|
2210
|
-
channel: readNonEmptyString(body.data.channel) ?? "ui",
|
|
2211
|
-
chatId: readNonEmptyString(body.data.chatId) ?? "web-ui",
|
|
2212
|
-
...requestedAgentId ? { agentId: requestedAgentId } : {},
|
|
2213
|
-
...requestedModel ? { model: requestedModel } : {},
|
|
2214
|
-
...metadata ? { metadata } : {}
|
|
2215
|
-
};
|
|
2216
|
-
try {
|
|
2217
|
-
const result = await this.options.chatRuntime.processTurn(request);
|
|
2218
|
-
const response = buildChatTurnView({
|
|
2219
|
-
result,
|
|
2220
|
-
fallbackSessionKey: sessionKey,
|
|
2221
|
-
requestedAgentId,
|
|
2222
|
-
requestedModel,
|
|
2223
|
-
requestedAt,
|
|
2224
|
-
startedAtMs
|
|
2225
|
-
});
|
|
2226
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
2227
|
-
return c.json(ok(response));
|
|
2228
|
-
} catch (error) {
|
|
2229
|
-
return c.json(err("CHAT_TURN_FAILED", formatUserFacingError(error)), 500);
|
|
2230
|
-
}
|
|
2231
|
-
};
|
|
2232
|
-
stopTurn = async (c) => {
|
|
2233
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2234
|
-
if (!chatRuntime?.stopTurn) {
|
|
2235
|
-
return c.json(err("NOT_AVAILABLE", "chat turn stop is not supported by runtime"), 503);
|
|
2236
|
-
}
|
|
2237
|
-
const body = await readJson(c.req.raw);
|
|
2238
|
-
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
2239
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
2240
|
-
}
|
|
2241
|
-
const runId = readNonEmptyString(body.data.runId);
|
|
2242
|
-
if (!runId) {
|
|
2243
|
-
return c.json(err("INVALID_BODY", "runId is required"), 400);
|
|
2244
|
-
}
|
|
2245
|
-
const request = {
|
|
2246
|
-
runId,
|
|
2247
|
-
...readNonEmptyString(body.data.sessionKey) ? { sessionKey: readNonEmptyString(body.data.sessionKey) } : {},
|
|
2248
|
-
...readNonEmptyString(body.data.agentId) ? { agentId: readNonEmptyString(body.data.agentId) } : {}
|
|
2249
|
-
};
|
|
2250
|
-
try {
|
|
2251
|
-
const result = await chatRuntime.stopTurn(request);
|
|
2252
|
-
return c.json(ok(result));
|
|
2253
|
-
} catch (error) {
|
|
2254
|
-
return c.json(err("CHAT_TURN_STOP_FAILED", String(error)), 500);
|
|
2255
|
-
}
|
|
2256
|
-
};
|
|
2257
|
-
streamTurn = async (c) => {
|
|
2258
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2259
|
-
if (!chatRuntime) {
|
|
2260
|
-
return c.json(err("NOT_AVAILABLE", "chat runtime unavailable"), 503);
|
|
2261
|
-
}
|
|
2262
|
-
const body = await readJson(c.req.raw);
|
|
2263
|
-
if (!body.ok) {
|
|
2264
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
2265
|
-
}
|
|
2266
|
-
const message = readNonEmptyString(body.data.message);
|
|
2267
|
-
if (!message) {
|
|
2268
|
-
return c.json(err("INVALID_BODY", "message is required"), 400);
|
|
2269
|
-
}
|
|
2270
|
-
const sessionKey = readNonEmptyString(body.data.sessionKey) ?? `ui:${Date.now().toString(36)}:${Math.random().toString(36).slice(2, 8)}`;
|
|
2271
|
-
const requestedAt = /* @__PURE__ */ new Date();
|
|
2272
|
-
const startedAtMs = requestedAt.getTime();
|
|
2273
|
-
const metadata = isRecord(body.data.metadata) ? body.data.metadata : void 0;
|
|
2274
|
-
const requestedAgentId = readNonEmptyString(body.data.agentId) ?? resolveAgentIdFromSessionKey(sessionKey);
|
|
2275
|
-
const requestedModel = readNonEmptyString(body.data.model);
|
|
2276
|
-
let runId = createChatRunId();
|
|
2277
|
-
const supportsManagedRuns = Boolean(chatRuntime.startTurnRun && chatRuntime.streamRun);
|
|
2278
|
-
let stopCapabilities = { stopSupported: Boolean(chatRuntime.stopTurn) };
|
|
2279
|
-
if (chatRuntime.getCapabilities) {
|
|
2280
|
-
try {
|
|
2281
|
-
stopCapabilities = await chatRuntime.getCapabilities({
|
|
2282
|
-
sessionKey,
|
|
2283
|
-
...requestedAgentId ? { agentId: requestedAgentId } : {}
|
|
2284
|
-
});
|
|
2285
|
-
} catch {
|
|
2286
|
-
stopCapabilities = {
|
|
2287
|
-
stopSupported: false,
|
|
2288
|
-
stopReason: "failed to resolve runtime stop capability"
|
|
2289
|
-
};
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
const request = {
|
|
2293
|
-
message,
|
|
2294
|
-
sessionKey,
|
|
2295
|
-
channel: readNonEmptyString(body.data.channel) ?? "ui",
|
|
2296
|
-
chatId: readNonEmptyString(body.data.chatId) ?? "web-ui",
|
|
2297
|
-
runId,
|
|
2298
|
-
...requestedAgentId ? { agentId: requestedAgentId } : {},
|
|
2299
|
-
...requestedModel ? { model: requestedModel } : {},
|
|
2300
|
-
...metadata ? { metadata } : {}
|
|
2301
|
-
};
|
|
2302
|
-
let managedRun = null;
|
|
2303
|
-
if (supportsManagedRuns && chatRuntime.startTurnRun) {
|
|
2304
|
-
try {
|
|
2305
|
-
managedRun = await chatRuntime.startTurnRun(request);
|
|
2306
|
-
} catch (error) {
|
|
2307
|
-
return c.json(err("CHAT_TURN_FAILED", formatUserFacingError(error)), 500);
|
|
2308
|
-
}
|
|
2309
|
-
if (readNonEmptyString(managedRun.runId)) {
|
|
2310
|
-
runId = readNonEmptyString(managedRun.runId);
|
|
2311
|
-
}
|
|
2312
|
-
stopCapabilities = {
|
|
2313
|
-
stopSupported: managedRun.stopSupported,
|
|
2314
|
-
...readNonEmptyString(managedRun.stopReason) ? { stopReason: readNonEmptyString(managedRun.stopReason) } : {}
|
|
2315
|
-
};
|
|
2316
|
-
}
|
|
2317
|
-
const encoder = new TextEncoder();
|
|
2318
|
-
const stream = new ReadableStream({
|
|
2319
|
-
start: async (controller) => {
|
|
2320
|
-
const push = (event, data) => {
|
|
2321
|
-
controller.enqueue(encoder.encode(toSseFrame(event, data)));
|
|
2322
|
-
};
|
|
2323
|
-
try {
|
|
2324
|
-
push("ready", {
|
|
2325
|
-
sessionKey: managedRun?.sessionKey ?? sessionKey,
|
|
2326
|
-
requestedAt: managedRun?.requestedAt ?? requestedAt.toISOString(),
|
|
2327
|
-
runId,
|
|
2328
|
-
stopSupported: stopCapabilities.stopSupported,
|
|
2329
|
-
...readNonEmptyString(stopCapabilities.stopReason) ? { stopReason: readNonEmptyString(stopCapabilities.stopReason) } : {}
|
|
2330
|
-
});
|
|
2331
|
-
if (supportsManagedRuns && chatRuntime.streamRun) {
|
|
2332
|
-
let hasFinal2 = false;
|
|
2333
|
-
for await (const event of chatRuntime.streamRun({ runId })) {
|
|
2334
|
-
const typed = event;
|
|
2335
|
-
if (typed.type === "delta") {
|
|
2336
|
-
if (typed.delta) {
|
|
2337
|
-
push("delta", { delta: typed.delta });
|
|
2338
|
-
}
|
|
2339
|
-
continue;
|
|
2340
|
-
}
|
|
2341
|
-
if (typed.type === "session_event") {
|
|
2342
|
-
push("session_event", typed.event);
|
|
2343
|
-
continue;
|
|
2344
|
-
}
|
|
2345
|
-
if (typed.type === "final") {
|
|
2346
|
-
const latestRun = chatRuntime.getRun ? await chatRuntime.getRun({ runId }) : null;
|
|
2347
|
-
const response = latestRun ? buildChatTurnViewFromRun({
|
|
2348
|
-
run: latestRun,
|
|
2349
|
-
fallbackSessionKey: sessionKey,
|
|
2350
|
-
fallbackAgentId: requestedAgentId,
|
|
2351
|
-
fallbackModel: requestedModel,
|
|
2352
|
-
fallbackReply: typed.result.reply
|
|
2353
|
-
}) : buildChatTurnView({
|
|
2354
|
-
result: typed.result,
|
|
2355
|
-
fallbackSessionKey: sessionKey,
|
|
2356
|
-
requestedAgentId,
|
|
2357
|
-
requestedModel,
|
|
2358
|
-
requestedAt,
|
|
2359
|
-
startedAtMs
|
|
2360
|
-
});
|
|
2361
|
-
hasFinal2 = true;
|
|
2362
|
-
push("final", response);
|
|
2363
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
2364
|
-
continue;
|
|
2365
|
-
}
|
|
2366
|
-
if (typed.type === "error") {
|
|
2367
|
-
push("error", {
|
|
2368
|
-
code: "CHAT_TURN_FAILED",
|
|
2369
|
-
message: formatUserFacingError(typed.error)
|
|
2370
|
-
});
|
|
2371
|
-
return;
|
|
2372
|
-
}
|
|
2373
|
-
}
|
|
2374
|
-
if (!hasFinal2) {
|
|
2375
|
-
push("error", {
|
|
2376
|
-
code: "CHAT_TURN_FAILED",
|
|
2377
|
-
message: "stream ended without a final result"
|
|
2378
|
-
});
|
|
2379
|
-
return;
|
|
2380
|
-
}
|
|
2381
|
-
push("done", { ok: true });
|
|
2382
|
-
return;
|
|
2383
|
-
}
|
|
2384
|
-
const streamTurn = chatRuntime.processTurnStream;
|
|
2385
|
-
if (!streamTurn) {
|
|
2386
|
-
const result = await chatRuntime.processTurn(request);
|
|
2387
|
-
const response = buildChatTurnView({
|
|
2388
|
-
result,
|
|
2389
|
-
fallbackSessionKey: sessionKey,
|
|
2390
|
-
requestedAgentId,
|
|
2391
|
-
requestedModel,
|
|
2392
|
-
requestedAt,
|
|
2393
|
-
startedAtMs
|
|
2394
|
-
});
|
|
2395
|
-
push("final", response);
|
|
2396
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
2397
|
-
push("done", { ok: true });
|
|
2398
|
-
return;
|
|
2399
|
-
}
|
|
2400
|
-
let hasFinal = false;
|
|
2401
|
-
for await (const event of streamTurn(request)) {
|
|
2402
|
-
const typed = event;
|
|
2403
|
-
if (typed.type === "delta") {
|
|
2404
|
-
if (typed.delta) {
|
|
2405
|
-
push("delta", { delta: typed.delta });
|
|
2406
|
-
}
|
|
2407
|
-
continue;
|
|
2408
|
-
}
|
|
2409
|
-
if (typed.type === "session_event") {
|
|
2410
|
-
push("session_event", typed.event);
|
|
2411
|
-
continue;
|
|
2412
|
-
}
|
|
2413
|
-
if (typed.type === "final") {
|
|
2414
|
-
const response = buildChatTurnView({
|
|
2415
|
-
result: typed.result,
|
|
2416
|
-
fallbackSessionKey: sessionKey,
|
|
2417
|
-
requestedAgentId,
|
|
2418
|
-
requestedModel,
|
|
2419
|
-
requestedAt,
|
|
2420
|
-
startedAtMs
|
|
2421
|
-
});
|
|
2422
|
-
hasFinal = true;
|
|
2423
|
-
push("final", response);
|
|
2424
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
2425
|
-
continue;
|
|
2426
|
-
}
|
|
2427
|
-
if (typed.type === "error") {
|
|
2428
|
-
push("error", {
|
|
2429
|
-
code: "CHAT_TURN_FAILED",
|
|
2430
|
-
message: formatUserFacingError(typed.error)
|
|
2431
|
-
});
|
|
2432
|
-
return;
|
|
2433
|
-
}
|
|
2434
|
-
}
|
|
2435
|
-
if (!hasFinal) {
|
|
2436
|
-
push("error", {
|
|
2437
|
-
code: "CHAT_TURN_FAILED",
|
|
2438
|
-
message: "stream ended without a final result"
|
|
2439
|
-
});
|
|
2440
|
-
return;
|
|
2441
|
-
}
|
|
2442
|
-
push("done", { ok: true });
|
|
2443
|
-
} catch (error) {
|
|
2444
|
-
push("error", {
|
|
2445
|
-
code: "CHAT_TURN_FAILED",
|
|
2446
|
-
message: formatUserFacingError(error)
|
|
2447
|
-
});
|
|
2448
|
-
} finally {
|
|
2449
|
-
controller.close();
|
|
2450
|
-
}
|
|
2451
|
-
}
|
|
2452
|
-
});
|
|
2453
|
-
return new Response(stream, {
|
|
2454
|
-
status: 200,
|
|
2455
|
-
headers: {
|
|
2456
|
-
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2457
|
-
"Cache-Control": "no-cache, no-transform",
|
|
2458
|
-
"Connection": "keep-alive",
|
|
2459
|
-
"X-Accel-Buffering": "no"
|
|
2460
|
-
}
|
|
2461
|
-
});
|
|
2462
|
-
};
|
|
2463
|
-
listRuns = async (c) => {
|
|
2464
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2465
|
-
if (!chatRuntime?.listRuns) {
|
|
2466
|
-
return c.json(err("NOT_AVAILABLE", "chat run management unavailable"), 503);
|
|
2467
|
-
}
|
|
2468
|
-
const query = c.req.query();
|
|
2469
|
-
const sessionKey = readNonEmptyString(query.sessionKey);
|
|
2470
|
-
const states = readChatRunStates(query.states);
|
|
2471
|
-
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
2472
|
-
try {
|
|
2473
|
-
const data = await chatRuntime.listRuns({
|
|
2474
|
-
...sessionKey ? { sessionKey } : {},
|
|
2475
|
-
...states ? { states } : {},
|
|
2476
|
-
...Number.isFinite(limit) ? { limit } : {}
|
|
2477
|
-
});
|
|
2478
|
-
return c.json(ok(data));
|
|
2479
|
-
} catch (error) {
|
|
2480
|
-
return c.json(err("CHAT_RUN_QUERY_FAILED", String(error)), 500);
|
|
2481
|
-
}
|
|
2482
|
-
};
|
|
2483
|
-
getRun = async (c) => {
|
|
2484
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2485
|
-
if (!chatRuntime?.getRun) {
|
|
2486
|
-
return c.json(err("NOT_AVAILABLE", "chat run management unavailable"), 503);
|
|
2487
|
-
}
|
|
2488
|
-
const runId = readNonEmptyString(c.req.param("runId"));
|
|
2489
|
-
if (!runId) {
|
|
2490
|
-
return c.json(err("INVALID_PATH", "runId is required"), 400);
|
|
2491
|
-
}
|
|
2492
|
-
try {
|
|
2493
|
-
const run = await chatRuntime.getRun({ runId });
|
|
2494
|
-
if (!run) {
|
|
2495
|
-
return c.json(err("NOT_FOUND", `chat run not found: ${runId}`), 404);
|
|
2496
|
-
}
|
|
2497
|
-
return c.json(ok(run));
|
|
2498
|
-
} catch (error) {
|
|
2499
|
-
return c.json(err("CHAT_RUN_QUERY_FAILED", String(error)), 500);
|
|
2500
|
-
}
|
|
2501
|
-
};
|
|
2502
|
-
streamRun = async (c) => {
|
|
2503
|
-
const chatRuntime = this.options.chatRuntime;
|
|
2504
|
-
const streamRun = chatRuntime?.streamRun;
|
|
2505
|
-
const getRun = chatRuntime?.getRun;
|
|
2506
|
-
if (!streamRun || !getRun) {
|
|
2507
|
-
return c.json(err("NOT_AVAILABLE", "chat run stream unavailable"), 503);
|
|
2508
|
-
}
|
|
2509
|
-
const runId = readNonEmptyString(c.req.param("runId"));
|
|
2510
|
-
if (!runId) {
|
|
2511
|
-
return c.json(err("INVALID_PATH", "runId is required"), 400);
|
|
2512
|
-
}
|
|
2513
|
-
const query = c.req.query();
|
|
2514
|
-
const fromEventIndex = typeof query.fromEventIndex === "string" ? Number.parseInt(query.fromEventIndex, 10) : void 0;
|
|
2515
|
-
const run = await getRun({ runId });
|
|
2516
|
-
if (!run) {
|
|
2517
|
-
return c.json(err("NOT_FOUND", `chat run not found: ${runId}`), 404);
|
|
2518
|
-
}
|
|
2519
|
-
const encoder = new TextEncoder();
|
|
2520
|
-
const stream = new ReadableStream({
|
|
2521
|
-
start: async (controller) => {
|
|
2522
|
-
const push = (event, data) => {
|
|
2523
|
-
controller.enqueue(encoder.encode(toSseFrame(event, data)));
|
|
2524
|
-
};
|
|
2525
|
-
try {
|
|
2526
|
-
push("ready", {
|
|
2527
|
-
sessionKey: run.sessionKey,
|
|
2528
|
-
requestedAt: run.requestedAt,
|
|
2529
|
-
runId: run.runId,
|
|
2530
|
-
stopSupported: run.stopSupported,
|
|
2531
|
-
...readNonEmptyString(run.stopReason) ? { stopReason: readNonEmptyString(run.stopReason) } : {}
|
|
2532
|
-
});
|
|
2533
|
-
let hasFinal = false;
|
|
2534
|
-
for await (const event of streamRun({
|
|
2535
|
-
runId: run.runId,
|
|
2536
|
-
...Number.isFinite(fromEventIndex) ? { fromEventIndex } : {}
|
|
2537
|
-
})) {
|
|
2538
|
-
const typed = event;
|
|
2539
|
-
if (typed.type === "delta") {
|
|
2540
|
-
if (typed.delta) {
|
|
2541
|
-
push("delta", { delta: typed.delta });
|
|
2542
|
-
}
|
|
2543
|
-
continue;
|
|
2544
|
-
}
|
|
2545
|
-
if (typed.type === "session_event") {
|
|
2546
|
-
push("session_event", typed.event);
|
|
2547
|
-
continue;
|
|
2548
|
-
}
|
|
2549
|
-
if (typed.type === "final") {
|
|
2550
|
-
const latestRun = await getRun({ runId: run.runId });
|
|
2551
|
-
const response = latestRun ? buildChatTurnViewFromRun({
|
|
2552
|
-
run: latestRun,
|
|
2553
|
-
fallbackSessionKey: run.sessionKey,
|
|
2554
|
-
fallbackAgentId: run.agentId,
|
|
2555
|
-
fallbackModel: run.model,
|
|
2556
|
-
fallbackReply: typed.result.reply
|
|
2557
|
-
}) : buildChatTurnView({
|
|
2558
|
-
result: typed.result,
|
|
2559
|
-
fallbackSessionKey: run.sessionKey,
|
|
2560
|
-
requestedAgentId: run.agentId,
|
|
2561
|
-
requestedModel: run.model,
|
|
2562
|
-
requestedAt: new Date(run.requestedAt),
|
|
2563
|
-
startedAtMs: Date.parse(run.requestedAt)
|
|
2564
|
-
});
|
|
2565
|
-
hasFinal = true;
|
|
2566
|
-
push("final", response);
|
|
2567
|
-
continue;
|
|
2568
|
-
}
|
|
2569
|
-
if (typed.type === "error") {
|
|
2570
|
-
push("error", {
|
|
2571
|
-
code: "CHAT_TURN_FAILED",
|
|
2572
|
-
message: formatUserFacingError(typed.error)
|
|
2573
|
-
});
|
|
2574
|
-
return;
|
|
2575
|
-
}
|
|
2576
|
-
}
|
|
2577
|
-
if (!hasFinal) {
|
|
2578
|
-
const latestRun = await getRun({ runId: run.runId });
|
|
2579
|
-
if (latestRun?.state === "failed") {
|
|
2580
|
-
push("error", {
|
|
2581
|
-
code: "CHAT_TURN_FAILED",
|
|
2582
|
-
message: formatUserFacingError(latestRun.error ?? "chat run failed")
|
|
2583
|
-
});
|
|
2584
|
-
return;
|
|
2585
|
-
}
|
|
2586
|
-
}
|
|
2587
|
-
push("done", { ok: true });
|
|
2588
|
-
} catch (error) {
|
|
2589
|
-
push("error", {
|
|
2590
|
-
code: "CHAT_TURN_FAILED",
|
|
2591
|
-
message: formatUserFacingError(error)
|
|
2592
|
-
});
|
|
2593
|
-
} finally {
|
|
2594
|
-
controller.close();
|
|
2595
|
-
}
|
|
2596
|
-
}
|
|
2597
|
-
});
|
|
2598
|
-
return new Response(stream, {
|
|
2599
|
-
status: 200,
|
|
2600
|
-
headers: {
|
|
2601
|
-
"Content-Type": "text/event-stream; charset=utf-8",
|
|
2602
|
-
"Cache-Control": "no-cache, no-transform",
|
|
2603
|
-
"Connection": "keep-alive",
|
|
2604
|
-
"X-Accel-Buffering": "no"
|
|
2605
|
-
}
|
|
2606
|
-
});
|
|
2607
|
-
};
|
|
2608
|
-
};
|
|
2609
|
-
|
|
2610
2035
|
// src/ui/channel-auth.ts
|
|
2611
2036
|
import { saveConfig as saveConfig3 } from "@nextclaw/core";
|
|
2612
2037
|
import {
|
|
@@ -3726,7 +3151,7 @@ var NcpSessionRoutesController = class {
|
|
|
3726
3151
|
return c.json(ok(payload));
|
|
3727
3152
|
};
|
|
3728
3153
|
listSessions = async (c) => {
|
|
3729
|
-
const sessionApi = this.options.
|
|
3154
|
+
const sessionApi = this.options.ncpSessionService;
|
|
3730
3155
|
if (!sessionApi) {
|
|
3731
3156
|
return c.json(err("NOT_AVAILABLE", "ncp session api unavailable"), 503);
|
|
3732
3157
|
}
|
|
@@ -3740,7 +3165,7 @@ var NcpSessionRoutesController = class {
|
|
|
3740
3165
|
return c.json(ok(payload));
|
|
3741
3166
|
};
|
|
3742
3167
|
getSession = async (c) => {
|
|
3743
|
-
const sessionApi = this.options.
|
|
3168
|
+
const sessionApi = this.options.ncpSessionService;
|
|
3744
3169
|
if (!sessionApi) {
|
|
3745
3170
|
return c.json(err("NOT_AVAILABLE", "ncp session api unavailable"), 503);
|
|
3746
3171
|
}
|
|
@@ -3752,7 +3177,7 @@ var NcpSessionRoutesController = class {
|
|
|
3752
3177
|
return c.json(ok(session));
|
|
3753
3178
|
};
|
|
3754
3179
|
listSessionMessages = async (c) => {
|
|
3755
|
-
const sessionApi = this.options.
|
|
3180
|
+
const sessionApi = this.options.ncpSessionService;
|
|
3756
3181
|
if (!sessionApi) {
|
|
3757
3182
|
return c.json(err("NOT_AVAILABLE", "ncp session api unavailable"), 503);
|
|
3758
3183
|
}
|
|
@@ -3772,7 +3197,7 @@ var NcpSessionRoutesController = class {
|
|
|
3772
3197
|
return c.json(ok(payload));
|
|
3773
3198
|
};
|
|
3774
3199
|
patchSession = async (c) => {
|
|
3775
|
-
const sessionApi = this.options.
|
|
3200
|
+
const sessionApi = this.options.ncpSessionService;
|
|
3776
3201
|
if (!sessionApi) {
|
|
3777
3202
|
return c.json(err("NOT_AVAILABLE", "ncp session api unavailable"), 503);
|
|
3778
3203
|
}
|
|
@@ -3821,7 +3246,7 @@ var NcpSessionRoutesController = class {
|
|
|
3821
3246
|
return c.json(ok(updated));
|
|
3822
3247
|
};
|
|
3823
3248
|
deleteSession = async (c) => {
|
|
3824
|
-
const sessionApi = this.options.
|
|
3249
|
+
const sessionApi = this.options.ncpSessionService;
|
|
3825
3250
|
if (!sessionApi) {
|
|
3826
3251
|
return c.json(err("NOT_AVAILABLE", "ncp session api unavailable"), 503);
|
|
3827
3252
|
}
|
|
@@ -4341,7 +3766,7 @@ var McpMarketplaceController = class {
|
|
|
4341
3766
|
};
|
|
4342
3767
|
|
|
4343
3768
|
// src/ui/router/marketplace/installed.ts
|
|
4344
|
-
import * as
|
|
3769
|
+
import * as NextclawCore from "@nextclaw/core";
|
|
4345
3770
|
import { discoverPluginStatusReport } from "@nextclaw/openclaw-compat";
|
|
4346
3771
|
|
|
4347
3772
|
// src/ui/router/marketplace/spec.ts
|
|
@@ -4448,9 +3873,9 @@ function dedupeInstalledPluginRecordsByCanonicalSpec(records) {
|
|
|
4448
3873
|
}
|
|
4449
3874
|
|
|
4450
3875
|
// src/ui/router/marketplace/installed.ts
|
|
4451
|
-
var getWorkspacePathFromConfig3 =
|
|
3876
|
+
var getWorkspacePathFromConfig3 = NextclawCore.getWorkspacePathFromConfig;
|
|
4452
3877
|
function createSkillsLoader(workspace) {
|
|
4453
|
-
const ctor =
|
|
3878
|
+
const ctor = NextclawCore.SkillsLoader;
|
|
4454
3879
|
if (!ctor) {
|
|
4455
3880
|
return null;
|
|
4456
3881
|
}
|
|
@@ -5362,75 +4787,6 @@ var RemoteRoutesController = class {
|
|
|
5362
4787
|
};
|
|
5363
4788
|
};
|
|
5364
4789
|
|
|
5365
|
-
// src/ui/router/session.controller.ts
|
|
5366
|
-
var SessionRoutesController = class {
|
|
5367
|
-
constructor(options) {
|
|
5368
|
-
this.options = options;
|
|
5369
|
-
}
|
|
5370
|
-
listSessions = (c) => {
|
|
5371
|
-
const query = c.req.query();
|
|
5372
|
-
const q = typeof query.q === "string" ? query.q : void 0;
|
|
5373
|
-
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
5374
|
-
const activeMinutes = typeof query.activeMinutes === "string" ? Number.parseInt(query.activeMinutes, 10) : void 0;
|
|
5375
|
-
const data = listSessions(this.options.configPath, {
|
|
5376
|
-
q,
|
|
5377
|
-
limit: Number.isFinite(limit) ? limit : void 0,
|
|
5378
|
-
activeMinutes: Number.isFinite(activeMinutes) ? activeMinutes : void 0
|
|
5379
|
-
});
|
|
5380
|
-
return c.json(ok(data));
|
|
5381
|
-
};
|
|
5382
|
-
getSessionHistory = (c) => {
|
|
5383
|
-
const key = decodeURIComponent(c.req.param("key"));
|
|
5384
|
-
const query = c.req.query();
|
|
5385
|
-
const limit = typeof query.limit === "string" ? Number.parseInt(query.limit, 10) : void 0;
|
|
5386
|
-
const data = getSessionHistory(this.options.configPath, key, Number.isFinite(limit) ? limit : void 0);
|
|
5387
|
-
if (!data) {
|
|
5388
|
-
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
5389
|
-
}
|
|
5390
|
-
return c.json(ok(data));
|
|
5391
|
-
};
|
|
5392
|
-
patchSession = async (c) => {
|
|
5393
|
-
const key = decodeURIComponent(c.req.param("key"));
|
|
5394
|
-
const body = await readJson(c.req.raw);
|
|
5395
|
-
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
5396
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
5397
|
-
}
|
|
5398
|
-
let availableSessionTypes;
|
|
5399
|
-
if (Object.prototype.hasOwnProperty.call(body.data, "sessionType")) {
|
|
5400
|
-
const sessionTypes = await buildChatSessionTypesView(this.options.chatRuntime);
|
|
5401
|
-
availableSessionTypes = sessionTypes.options.map((item) => item.value);
|
|
5402
|
-
}
|
|
5403
|
-
let data;
|
|
5404
|
-
try {
|
|
5405
|
-
data = patchSession(this.options.configPath, key, body.data, {
|
|
5406
|
-
...availableSessionTypes ? { availableSessionTypes } : {}
|
|
5407
|
-
});
|
|
5408
|
-
} catch (error) {
|
|
5409
|
-
if (error instanceof SessionPatchValidationError) {
|
|
5410
|
-
if (error.code === "SESSION_TYPE_IMMUTABLE") {
|
|
5411
|
-
return c.json(err(error.code, error.message), 409);
|
|
5412
|
-
}
|
|
5413
|
-
return c.json(err(error.code, error.message), 400);
|
|
5414
|
-
}
|
|
5415
|
-
throw error;
|
|
5416
|
-
}
|
|
5417
|
-
if (!data) {
|
|
5418
|
-
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
5419
|
-
}
|
|
5420
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
5421
|
-
return c.json(ok(data));
|
|
5422
|
-
};
|
|
5423
|
-
deleteSession = (c) => {
|
|
5424
|
-
const key = decodeURIComponent(c.req.param("key"));
|
|
5425
|
-
const deleted = deleteSession(this.options.configPath, key);
|
|
5426
|
-
if (!deleted) {
|
|
5427
|
-
return c.json(err("NOT_FOUND", `session not found: ${key}`), 404);
|
|
5428
|
-
}
|
|
5429
|
-
this.options.publish({ type: "config.updated", payload: { path: "session" } });
|
|
5430
|
-
return c.json(ok({ deleted: true }));
|
|
5431
|
-
};
|
|
5432
|
-
};
|
|
5433
|
-
|
|
5434
4790
|
// src/ui/router.ts
|
|
5435
4791
|
function registerAuthRoutes(app, authController) {
|
|
5436
4792
|
app.get("/api/auth/status", authController.getStatus);
|
|
@@ -5461,24 +4817,15 @@ function registerConfigRoutes(app, configController) {
|
|
|
5461
4817
|
app.put("/api/config/runtime", configController.updateRuntime);
|
|
5462
4818
|
app.post("/api/config/actions/:actionId/execute", configController.executeAction);
|
|
5463
4819
|
}
|
|
5464
|
-
function
|
|
5465
|
-
app.get("/api/
|
|
5466
|
-
app.get("/api/
|
|
5467
|
-
app.get("/api/
|
|
5468
|
-
app.
|
|
5469
|
-
app.
|
|
5470
|
-
app.
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
app.get("/api/chat/runs/:runId/stream", chatController.streamRun);
|
|
5474
|
-
}
|
|
5475
|
-
function registerSessionRoutes(app, sessionController) {
|
|
5476
|
-
app.get("/api/sessions", sessionController.listSessions);
|
|
5477
|
-
app.get("/api/sessions/:key/history", sessionController.getSessionHistory);
|
|
5478
|
-
app.put("/api/sessions/:key", sessionController.patchSession);
|
|
5479
|
-
app.delete("/api/sessions/:key", sessionController.deleteSession);
|
|
5480
|
-
}
|
|
5481
|
-
function registerNcpRoutes(app, options, ncpSessionController, ncpAssetController) {
|
|
4820
|
+
function registerNcpSessionRoutes(app, ncpSessionController) {
|
|
4821
|
+
app.get("/api/ncp/session-types", ncpSessionController.getSessionTypes);
|
|
4822
|
+
app.get("/api/ncp/sessions", ncpSessionController.listSessions);
|
|
4823
|
+
app.get("/api/ncp/sessions/:sessionId", ncpSessionController.getSession);
|
|
4824
|
+
app.put("/api/ncp/sessions/:sessionId", ncpSessionController.patchSession);
|
|
4825
|
+
app.get("/api/ncp/sessions/:sessionId/messages", ncpSessionController.listSessionMessages);
|
|
4826
|
+
app.delete("/api/ncp/sessions/:sessionId", ncpSessionController.deleteSession);
|
|
4827
|
+
}
|
|
4828
|
+
function registerNcpRuntimeRoutes(app, options, ncpAssetController) {
|
|
5482
4829
|
if (!options.ncpAgent) {
|
|
5483
4830
|
return;
|
|
5484
4831
|
}
|
|
@@ -5487,12 +4834,6 @@ function registerNcpRoutes(app, options, ncpSessionController, ncpAssetControlle
|
|
|
5487
4834
|
agentClientEndpoint: options.ncpAgent.agentClientEndpoint,
|
|
5488
4835
|
streamProvider: options.ncpAgent.streamProvider
|
|
5489
4836
|
});
|
|
5490
|
-
app.get("/api/ncp/session-types", ncpSessionController.getSessionTypes);
|
|
5491
|
-
app.get("/api/ncp/sessions", ncpSessionController.listSessions);
|
|
5492
|
-
app.get("/api/ncp/sessions/:sessionId", ncpSessionController.getSession);
|
|
5493
|
-
app.put("/api/ncp/sessions/:sessionId", ncpSessionController.patchSession);
|
|
5494
|
-
app.get("/api/ncp/sessions/:sessionId/messages", ncpSessionController.listSessionMessages);
|
|
5495
|
-
app.delete("/api/ncp/sessions/:sessionId", ncpSessionController.deleteSession);
|
|
5496
4837
|
app.post("/api/ncp/assets", ncpAssetController.putAssets);
|
|
5497
4838
|
app.get("/api/ncp/assets/content", ncpAssetController.getAssetContent);
|
|
5498
4839
|
}
|
|
@@ -5522,8 +4863,6 @@ function createUiRouter(options) {
|
|
|
5522
4863
|
const appController = new AppRoutesController(options);
|
|
5523
4864
|
const authController = new AuthRoutesController(authService);
|
|
5524
4865
|
const configController = new ConfigRoutesController(options);
|
|
5525
|
-
const chatController = new ChatRoutesController(options);
|
|
5526
|
-
const sessionController = new SessionRoutesController(options);
|
|
5527
4866
|
const cronController = new CronRoutesController(options);
|
|
5528
4867
|
const ncpSessionController = new NcpSessionRoutesController(options);
|
|
5529
4868
|
const ncpAssetController = new NcpAssetRoutesController(options);
|
|
@@ -5534,7 +4873,7 @@ function createUiRouter(options) {
|
|
|
5534
4873
|
app.notFound((c) => c.json(err("NOT_FOUND", "endpoint not found"), 404));
|
|
5535
4874
|
app.use("/api/*", async (c, next) => {
|
|
5536
4875
|
const path = c.req.path;
|
|
5537
|
-
if (path === "/api/health" || path.startsWith("/api/auth/")) {
|
|
4876
|
+
if (path === "/api/health" || path === "/api/runtime/bootstrap-status" || path.startsWith("/api/auth/")) {
|
|
5538
4877
|
await next();
|
|
5539
4878
|
return;
|
|
5540
4879
|
}
|
|
@@ -5547,11 +4886,11 @@ function createUiRouter(options) {
|
|
|
5547
4886
|
});
|
|
5548
4887
|
app.get("/api/health", appController.health);
|
|
5549
4888
|
app.get("/api/app/meta", appController.appMeta);
|
|
4889
|
+
app.get("/api/runtime/bootstrap-status", appController.bootstrapStatus);
|
|
5550
4890
|
registerAuthRoutes(app, authController);
|
|
5551
4891
|
registerConfigRoutes(app, configController);
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
registerNcpRoutes(app, options, ncpSessionController, ncpAssetController);
|
|
4892
|
+
registerNcpSessionRoutes(app, ncpSessionController);
|
|
4893
|
+
registerNcpRuntimeRoutes(app, options, ncpAssetController);
|
|
5555
4894
|
registerCronRoutes(app, cronController);
|
|
5556
4895
|
registerRemoteRoutes(app, remoteController);
|
|
5557
4896
|
mountMarketplaceRoutes(app, {
|
|
@@ -5660,10 +4999,11 @@ function startUiServer(options) {
|
|
|
5660
4999
|
applyLiveConfigReload: options.applyLiveConfigReload,
|
|
5661
5000
|
marketplace: options.marketplace,
|
|
5662
5001
|
cronService: options.cronService,
|
|
5663
|
-
chatRuntime: options.chatRuntime,
|
|
5664
5002
|
ncpAgent: options.ncpAgent,
|
|
5003
|
+
ncpSessionService: options.ncpSessionService,
|
|
5665
5004
|
authService,
|
|
5666
5005
|
remoteAccess: options.remoteAccess,
|
|
5006
|
+
getBootstrapStatus: options.getBootstrapStatus,
|
|
5667
5007
|
getPluginChannelBindings: options.getPluginChannelBindings,
|
|
5668
5008
|
getPluginUiMetadata: options.getPluginUiMetadata
|
|
5669
5009
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/server",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Nextclaw UI/API server.",
|
|
6
6
|
"type": "module",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"@hono/node-server": "^1.13.3",
|
|
19
19
|
"hono": "^4.6.2",
|
|
20
20
|
"ws": "^8.18.0",
|
|
21
|
-
"@nextclaw/mcp": "0.1.
|
|
21
|
+
"@nextclaw/mcp": "0.1.52",
|
|
22
|
+
"@nextclaw/ncp": "0.4.0",
|
|
23
|
+
"@nextclaw/runtime": "0.2.17",
|
|
24
|
+
"@nextclaw/core": "0.11.3",
|
|
22
25
|
"@nextclaw/ncp-http-agent-server": "0.3.4",
|
|
23
|
-
"@nextclaw/
|
|
24
|
-
"@nextclaw/openclaw-compat": "0.3.34",
|
|
25
|
-
"@nextclaw/core": "0.11.2",
|
|
26
|
-
"@nextclaw/ncp": "0.4.0"
|
|
26
|
+
"@nextclaw/openclaw-compat": "0.3.36"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/node": "^20.17.6",
|