@nextclaw/server 0.5.12 → 0.5.14
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 +45 -17
- package/dist/index.js +344 -178
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -324,37 +324,65 @@ type MarketplaceInstalledView = {
|
|
|
324
324
|
specs: string[];
|
|
325
325
|
records: MarketplaceInstalledRecord[];
|
|
326
326
|
};
|
|
327
|
-
type
|
|
328
|
-
|
|
329
|
-
spec: string;
|
|
327
|
+
type MarketplaceInstallSkillParams = {
|
|
328
|
+
slug: string;
|
|
330
329
|
kind?: MarketplaceInstallKind;
|
|
330
|
+
skill?: string;
|
|
331
|
+
installPath?: string;
|
|
331
332
|
version?: string;
|
|
332
333
|
registry?: string;
|
|
333
334
|
force?: boolean;
|
|
334
335
|
};
|
|
335
|
-
type
|
|
336
|
-
type
|
|
336
|
+
type MarketplacePluginInstallRequest = {
|
|
337
|
+
type?: "plugin";
|
|
337
338
|
spec: string;
|
|
338
|
-
message: string;
|
|
339
|
-
output?: string;
|
|
340
339
|
};
|
|
341
|
-
type
|
|
342
|
-
|
|
340
|
+
type MarketplaceSkillInstallRequest = {
|
|
341
|
+
type?: "skill";
|
|
342
|
+
spec: string;
|
|
343
343
|
kind?: MarketplaceInstallKind;
|
|
344
|
+
skill?: string;
|
|
345
|
+
installPath?: string;
|
|
344
346
|
version?: string;
|
|
345
347
|
registry?: string;
|
|
346
348
|
force?: boolean;
|
|
347
349
|
};
|
|
348
|
-
type
|
|
349
|
-
type
|
|
350
|
-
|
|
351
|
-
|
|
350
|
+
type MarketplacePluginInstallResult = {
|
|
351
|
+
type: "plugin";
|
|
352
|
+
spec: string;
|
|
353
|
+
message: string;
|
|
354
|
+
output?: string;
|
|
355
|
+
};
|
|
356
|
+
type MarketplaceSkillInstallResult = {
|
|
357
|
+
type: "skill";
|
|
358
|
+
spec: string;
|
|
359
|
+
message: string;
|
|
360
|
+
output?: string;
|
|
361
|
+
};
|
|
362
|
+
type MarketplacePluginManageAction = "enable" | "disable" | "uninstall";
|
|
363
|
+
type MarketplaceSkillManageAction = "uninstall";
|
|
364
|
+
type MarketplacePluginManageRequest = {
|
|
365
|
+
type?: "plugin";
|
|
366
|
+
action: MarketplacePluginManageAction;
|
|
352
367
|
id?: string;
|
|
353
368
|
spec?: string;
|
|
354
369
|
};
|
|
355
|
-
type
|
|
356
|
-
type
|
|
357
|
-
action:
|
|
370
|
+
type MarketplaceSkillManageRequest = {
|
|
371
|
+
type?: "skill";
|
|
372
|
+
action: MarketplaceSkillManageAction;
|
|
373
|
+
id?: string;
|
|
374
|
+
spec?: string;
|
|
375
|
+
};
|
|
376
|
+
type MarketplacePluginManageResult = {
|
|
377
|
+
type: "plugin";
|
|
378
|
+
action: MarketplacePluginManageAction;
|
|
379
|
+
id: string;
|
|
380
|
+
message: string;
|
|
381
|
+
output?: string;
|
|
382
|
+
};
|
|
383
|
+
type MarketplaceSkillManageResult = {
|
|
384
|
+
type: "skill";
|
|
385
|
+
action: MarketplaceSkillManageAction;
|
|
358
386
|
id: string;
|
|
359
387
|
message: string;
|
|
360
388
|
output?: string;
|
|
@@ -463,4 +491,4 @@ declare function patchSession(configPath: string, key: string, patch: SessionPat
|
|
|
463
491
|
declare function deleteSession(configPath: string, key: string): boolean;
|
|
464
492
|
declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
|
|
465
493
|
|
|
466
|
-
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, 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, type MarketplaceApiConfig, type MarketplaceInstallKind, type
|
|
494
|
+
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, 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, type MarketplaceApiConfig, type MarketplaceInstallKind, type MarketplaceInstallSkillParams, type MarketplaceInstallSpec, type MarketplaceInstalledRecord, type MarketplaceInstalledView, type MarketplaceInstaller, type MarketplaceItemSummary, type MarketplaceItemType, type MarketplaceItemView, type MarketplaceListView, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderConfigUpdate, type ProviderConfigView, type ProviderSpecView, type RuntimeConfigUpdate, type SessionConfigView, type SessionEntryView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, updateChannel, updateModel, updateProvider, updateRuntime };
|
package/dist/index.js
CHANGED
|
@@ -931,10 +931,19 @@ function collectInstalledSkillRecords(options) {
|
|
|
931
931
|
records
|
|
932
932
|
};
|
|
933
933
|
}
|
|
934
|
-
function
|
|
935
|
-
const installed =
|
|
934
|
+
function collectPluginMarketplaceInstalledView(options) {
|
|
935
|
+
const installed = collectInstalledPluginRecords(options);
|
|
936
936
|
return {
|
|
937
|
-
type,
|
|
937
|
+
type: "plugin",
|
|
938
|
+
total: installed.records.length,
|
|
939
|
+
specs: installed.specs,
|
|
940
|
+
records: installed.records
|
|
941
|
+
};
|
|
942
|
+
}
|
|
943
|
+
function collectSkillMarketplaceInstalledView(options) {
|
|
944
|
+
const installed = collectInstalledSkillRecords(options);
|
|
945
|
+
return {
|
|
946
|
+
type: "skill",
|
|
938
947
|
total: installed.records.length,
|
|
939
948
|
specs: installed.specs,
|
|
940
949
|
records: installed.records
|
|
@@ -993,9 +1002,15 @@ function collectKnownSkillNames(options) {
|
|
|
993
1002
|
const loader = createSkillsLoader(getWorkspacePathFromConfig3(config));
|
|
994
1003
|
return new Set((loader?.listSkills(false) ?? []).map((skill) => skill.name));
|
|
995
1004
|
}
|
|
996
|
-
function
|
|
997
|
-
|
|
998
|
-
|
|
1005
|
+
function isSupportedMarketplacePluginItem(item) {
|
|
1006
|
+
return item.type === "plugin" && item.install.kind === "npm" && isSupportedMarketplacePluginSpec(item.install.spec);
|
|
1007
|
+
}
|
|
1008
|
+
function isSupportedMarketplaceSkillItem(item, knownSkillNames) {
|
|
1009
|
+
if (item.type !== "skill") {
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
if (item.install.kind === "git") {
|
|
1013
|
+
return true;
|
|
999
1014
|
}
|
|
1000
1015
|
return item.install.kind === "builtin" && knownSkillNames.has(item.install.spec);
|
|
1001
1016
|
}
|
|
@@ -1033,226 +1048,377 @@ async function fetchAllMarketplaceItems(params) {
|
|
|
1033
1048
|
}
|
|
1034
1049
|
};
|
|
1035
1050
|
}
|
|
1036
|
-
async function
|
|
1037
|
-
|
|
1051
|
+
async function fetchAllPluginMarketplaceItems(params) {
|
|
1052
|
+
return await fetchAllMarketplaceItems({
|
|
1053
|
+
baseUrl: params.baseUrl,
|
|
1054
|
+
segment: "plugins",
|
|
1055
|
+
query: params.query
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
async function fetchAllSkillMarketplaceItems(params) {
|
|
1059
|
+
return await fetchAllMarketplaceItems({
|
|
1060
|
+
baseUrl: params.baseUrl,
|
|
1061
|
+
segment: "skills",
|
|
1062
|
+
query: params.query
|
|
1063
|
+
});
|
|
1064
|
+
}
|
|
1065
|
+
async function installMarketplacePlugin(params) {
|
|
1038
1066
|
const spec = typeof params.body.spec === "string" ? params.body.spec.trim() : "";
|
|
1039
|
-
if (
|
|
1040
|
-
throw new Error("INVALID_BODY:
|
|
1067
|
+
if (!spec) {
|
|
1068
|
+
throw new Error("INVALID_BODY:non-empty spec is required");
|
|
1041
1069
|
}
|
|
1042
1070
|
const installer = params.options.marketplace?.installer;
|
|
1043
1071
|
if (!installer) {
|
|
1044
1072
|
throw new Error("NOT_AVAILABLE:marketplace installer is not configured");
|
|
1045
1073
|
}
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
if (!installer.installPlugin) {
|
|
1049
|
-
throw new Error("NOT_AVAILABLE:plugin installer is not configured");
|
|
1050
|
-
}
|
|
1051
|
-
result = await installer.installPlugin(spec);
|
|
1052
|
-
} else {
|
|
1053
|
-
if (!installer.installSkill) {
|
|
1054
|
-
throw new Error("NOT_AVAILABLE:skill installer is not configured");
|
|
1055
|
-
}
|
|
1056
|
-
result = await installer.installSkill({
|
|
1057
|
-
slug: spec,
|
|
1058
|
-
kind: params.body.kind,
|
|
1059
|
-
version: params.body.version,
|
|
1060
|
-
registry: params.body.registry,
|
|
1061
|
-
force: params.body.force
|
|
1062
|
-
});
|
|
1074
|
+
if (!installer.installPlugin) {
|
|
1075
|
+
throw new Error("NOT_AVAILABLE:plugin installer is not configured");
|
|
1063
1076
|
}
|
|
1064
|
-
|
|
1077
|
+
const result = await installer.installPlugin(spec);
|
|
1078
|
+
params.options.publish({ type: "config.updated", payload: { path: "plugins" } });
|
|
1065
1079
|
return {
|
|
1066
|
-
type,
|
|
1080
|
+
type: "plugin",
|
|
1067
1081
|
spec,
|
|
1068
1082
|
message: result.message,
|
|
1069
1083
|
output: result.output
|
|
1070
1084
|
};
|
|
1071
1085
|
}
|
|
1072
|
-
async function
|
|
1073
|
-
const
|
|
1086
|
+
async function installMarketplaceSkill(params) {
|
|
1087
|
+
const spec = typeof params.body.spec === "string" ? params.body.spec.trim() : "";
|
|
1088
|
+
if (!spec) {
|
|
1089
|
+
throw new Error("INVALID_BODY:non-empty spec is required");
|
|
1090
|
+
}
|
|
1091
|
+
const installer = params.options.marketplace?.installer;
|
|
1092
|
+
if (!installer) {
|
|
1093
|
+
throw new Error("NOT_AVAILABLE:marketplace installer is not configured");
|
|
1094
|
+
}
|
|
1095
|
+
if (!installer.installSkill) {
|
|
1096
|
+
throw new Error("NOT_AVAILABLE:skill installer is not configured");
|
|
1097
|
+
}
|
|
1098
|
+
const result = await installer.installSkill({
|
|
1099
|
+
slug: spec,
|
|
1100
|
+
kind: params.body.kind,
|
|
1101
|
+
skill: params.body.skill,
|
|
1102
|
+
installPath: params.body.installPath,
|
|
1103
|
+
version: params.body.version,
|
|
1104
|
+
registry: params.body.registry,
|
|
1105
|
+
force: params.body.force
|
|
1106
|
+
});
|
|
1107
|
+
params.options.publish({ type: "config.updated", payload: { path: "skills" } });
|
|
1108
|
+
return {
|
|
1109
|
+
type: "skill",
|
|
1110
|
+
spec,
|
|
1111
|
+
message: result.message,
|
|
1112
|
+
output: result.output
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
async function manageMarketplacePlugin(params) {
|
|
1074
1116
|
const action = params.body.action;
|
|
1075
1117
|
const requestedTargetId = typeof params.body.id === "string" && params.body.id.trim().length > 0 ? params.body.id.trim() : typeof params.body.spec === "string" && params.body.spec.trim().length > 0 ? params.body.spec.trim() : "";
|
|
1076
1118
|
const rawSpec = typeof params.body.spec === "string" ? params.body.spec.trim() : "";
|
|
1077
|
-
const targetId =
|
|
1078
|
-
if (
|
|
1079
|
-
throw new Error("INVALID_BODY:
|
|
1119
|
+
const targetId = resolvePluginManageTargetId(params.options, requestedTargetId, rawSpec);
|
|
1120
|
+
if (action !== "enable" && action !== "disable" && action !== "uninstall" || !targetId) {
|
|
1121
|
+
throw new Error("INVALID_BODY:action and non-empty id/spec are required");
|
|
1080
1122
|
}
|
|
1081
1123
|
const installer = params.options.marketplace?.installer;
|
|
1082
1124
|
if (!installer) {
|
|
1083
1125
|
throw new Error("NOT_AVAILABLE:marketplace installer is not configured");
|
|
1084
1126
|
}
|
|
1085
1127
|
let result;
|
|
1086
|
-
if (
|
|
1087
|
-
if (
|
|
1088
|
-
|
|
1089
|
-
throw new Error("NOT_AVAILABLE:plugin enable is not configured");
|
|
1090
|
-
}
|
|
1091
|
-
result = await installer.enablePlugin(targetId);
|
|
1092
|
-
} else if (action === "disable") {
|
|
1093
|
-
if (!installer.disablePlugin) {
|
|
1094
|
-
throw new Error("NOT_AVAILABLE:plugin disable is not configured");
|
|
1095
|
-
}
|
|
1096
|
-
result = await installer.disablePlugin(targetId);
|
|
1097
|
-
} else {
|
|
1098
|
-
if (!installer.uninstallPlugin) {
|
|
1099
|
-
throw new Error("NOT_AVAILABLE:plugin uninstall is not configured");
|
|
1100
|
-
}
|
|
1101
|
-
result = await installer.uninstallPlugin(targetId);
|
|
1128
|
+
if (action === "enable") {
|
|
1129
|
+
if (!installer.enablePlugin) {
|
|
1130
|
+
throw new Error("NOT_AVAILABLE:plugin enable is not configured");
|
|
1102
1131
|
}
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1132
|
+
result = await installer.enablePlugin(targetId);
|
|
1133
|
+
} else if (action === "disable") {
|
|
1134
|
+
if (!installer.disablePlugin) {
|
|
1135
|
+
throw new Error("NOT_AVAILABLE:plugin disable is not configured");
|
|
1106
1136
|
}
|
|
1107
|
-
|
|
1108
|
-
|
|
1137
|
+
result = await installer.disablePlugin(targetId);
|
|
1138
|
+
} else {
|
|
1139
|
+
if (!installer.uninstallPlugin) {
|
|
1140
|
+
throw new Error("NOT_AVAILABLE:plugin uninstall is not configured");
|
|
1109
1141
|
}
|
|
1110
|
-
result = await installer.
|
|
1142
|
+
result = await installer.uninstallPlugin(targetId);
|
|
1111
1143
|
}
|
|
1112
|
-
params.options.publish({ type: "config.updated", payload: { path:
|
|
1144
|
+
params.options.publish({ type: "config.updated", payload: { path: "plugins" } });
|
|
1113
1145
|
return {
|
|
1114
|
-
type,
|
|
1146
|
+
type: "plugin",
|
|
1115
1147
|
action,
|
|
1116
1148
|
id: targetId,
|
|
1117
1149
|
message: result.message,
|
|
1118
1150
|
output: result.output
|
|
1119
1151
|
};
|
|
1120
1152
|
}
|
|
1121
|
-
function
|
|
1122
|
-
const
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1153
|
+
async function manageMarketplaceSkill(params) {
|
|
1154
|
+
const action = params.body.action;
|
|
1155
|
+
const targetId = typeof params.body.id === "string" && params.body.id.trim().length > 0 ? params.body.id.trim() : typeof params.body.spec === "string" && params.body.spec.trim().length > 0 ? params.body.spec.trim() : "";
|
|
1156
|
+
if (action !== "uninstall" || !targetId) {
|
|
1157
|
+
throw new Error("INVALID_BODY:skill manage requires uninstall action and non-empty id/spec");
|
|
1158
|
+
}
|
|
1159
|
+
const installer = params.options.marketplace?.installer;
|
|
1160
|
+
if (!installer) {
|
|
1161
|
+
throw new Error("NOT_AVAILABLE:marketplace installer is not configured");
|
|
1162
|
+
}
|
|
1163
|
+
if (!installer.uninstallSkill) {
|
|
1164
|
+
throw new Error("NOT_AVAILABLE:skill uninstall is not configured");
|
|
1165
|
+
}
|
|
1166
|
+
const result = await installer.uninstallSkill(targetId);
|
|
1167
|
+
params.options.publish({ type: "config.updated", payload: { path: "skills" } });
|
|
1168
|
+
return {
|
|
1169
|
+
type: "skill",
|
|
1170
|
+
action,
|
|
1171
|
+
id: targetId,
|
|
1172
|
+
message: result.message,
|
|
1173
|
+
output: result.output
|
|
1174
|
+
};
|
|
1175
|
+
}
|
|
1176
|
+
function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
1177
|
+
app.get("/api/marketplace/plugins/installed", (c) => {
|
|
1178
|
+
return c.json(ok(collectPluginMarketplaceInstalledView(options)));
|
|
1179
|
+
});
|
|
1180
|
+
app.get("/api/marketplace/plugins/items", async (c) => {
|
|
1181
|
+
const query = c.req.query();
|
|
1182
|
+
const result = await fetchAllPluginMarketplaceItems({
|
|
1183
|
+
baseUrl: marketplaceBaseUrl,
|
|
1184
|
+
query: {
|
|
1185
|
+
q: query.q,
|
|
1186
|
+
tag: query.tag,
|
|
1187
|
+
sort: query.sort,
|
|
1188
|
+
page: query.page,
|
|
1189
|
+
pageSize: query.pageSize
|
|
1145
1190
|
}
|
|
1146
|
-
const knownSkillNames = collectKnownSkillNames(options);
|
|
1147
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
|
|
1148
|
-
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1149
|
-
const requestedPage = toPositiveInt(query.page, 1);
|
|
1150
|
-
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
1151
|
-
const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
|
|
1152
|
-
return c.json(ok({
|
|
1153
|
-
total: filteredItems.length,
|
|
1154
|
-
page: currentPage,
|
|
1155
|
-
pageSize,
|
|
1156
|
-
totalPages,
|
|
1157
|
-
sort: result.data.sort,
|
|
1158
|
-
query: result.data.query,
|
|
1159
|
-
items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
1160
|
-
}));
|
|
1161
1191
|
});
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1192
|
+
if (!result.ok) {
|
|
1193
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1194
|
+
}
|
|
1195
|
+
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1196
|
+
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1197
|
+
const requestedPage = toPositiveInt(query.page, 1);
|
|
1198
|
+
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
1199
|
+
const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
|
|
1200
|
+
return c.json(ok({
|
|
1201
|
+
total: filteredItems.length,
|
|
1202
|
+
page: currentPage,
|
|
1203
|
+
pageSize,
|
|
1204
|
+
totalPages,
|
|
1205
|
+
sort: result.data.sort,
|
|
1206
|
+
query: result.data.query,
|
|
1207
|
+
items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
1208
|
+
}));
|
|
1209
|
+
});
|
|
1210
|
+
app.get("/api/marketplace/plugins/items/:slug", async (c) => {
|
|
1211
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1212
|
+
const result = await fetchMarketplaceData({
|
|
1213
|
+
baseUrl: marketplaceBaseUrl,
|
|
1214
|
+
path: `/api/v1/plugins/items/${slug}`
|
|
1215
|
+
});
|
|
1216
|
+
if (!result.ok) {
|
|
1217
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1218
|
+
}
|
|
1219
|
+
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1220
|
+
if (!isSupportedMarketplacePluginItem(sanitized)) {
|
|
1221
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1222
|
+
}
|
|
1223
|
+
return c.json(ok(sanitized));
|
|
1224
|
+
});
|
|
1225
|
+
app.post("/api/marketplace/plugins/install", async (c) => {
|
|
1226
|
+
const body = await readJson(c.req.raw);
|
|
1227
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1228
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1229
|
+
}
|
|
1230
|
+
if (body.data.type && body.data.type !== "plugin") {
|
|
1231
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1232
|
+
}
|
|
1233
|
+
try {
|
|
1234
|
+
const payload = await installMarketplacePlugin({
|
|
1235
|
+
options,
|
|
1236
|
+
body: body.data
|
|
1167
1237
|
});
|
|
1168
|
-
|
|
1169
|
-
|
|
1238
|
+
return c.json(ok(payload));
|
|
1239
|
+
} catch (error) {
|
|
1240
|
+
const message = String(error);
|
|
1241
|
+
if (message.startsWith("INVALID_BODY:")) {
|
|
1242
|
+
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1170
1243
|
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
if (!isSupportedMarketplaceItem(sanitized, knownSkillNames)) {
|
|
1174
|
-
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1244
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1245
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1175
1246
|
}
|
|
1176
|
-
return c.json(
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1247
|
+
return c.json(err("INSTALL_FAILED", message), 400);
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
app.post("/api/marketplace/plugins/manage", async (c) => {
|
|
1251
|
+
const body = await readJson(c.req.raw);
|
|
1252
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1253
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1254
|
+
}
|
|
1255
|
+
if (body.data.type && body.data.type !== "plugin") {
|
|
1256
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1257
|
+
}
|
|
1258
|
+
try {
|
|
1259
|
+
const payload = await manageMarketplacePlugin({
|
|
1260
|
+
options,
|
|
1261
|
+
body: body.data
|
|
1262
|
+
});
|
|
1263
|
+
return c.json(ok(payload));
|
|
1264
|
+
} catch (error) {
|
|
1265
|
+
const message = String(error);
|
|
1266
|
+
if (message.startsWith("INVALID_BODY:")) {
|
|
1267
|
+
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1182
1268
|
}
|
|
1183
|
-
if (
|
|
1184
|
-
return c.json(err("
|
|
1269
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1270
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1185
1271
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
if (message.startsWith("INVALID_BODY:")) {
|
|
1198
|
-
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1199
|
-
}
|
|
1200
|
-
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1201
|
-
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1202
|
-
}
|
|
1203
|
-
return c.json(err("INSTALL_FAILED", message), 400);
|
|
1272
|
+
return c.json(err("MANAGE_FAILED", message), 400);
|
|
1273
|
+
}
|
|
1274
|
+
});
|
|
1275
|
+
app.get("/api/marketplace/plugins/recommendations", async (c) => {
|
|
1276
|
+
const query = c.req.query();
|
|
1277
|
+
const result = await fetchMarketplaceData({
|
|
1278
|
+
baseUrl: marketplaceBaseUrl,
|
|
1279
|
+
path: "/api/v1/plugins/recommendations",
|
|
1280
|
+
query: {
|
|
1281
|
+
scene: query.scene,
|
|
1282
|
+
limit: query.limit
|
|
1204
1283
|
}
|
|
1205
1284
|
});
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1285
|
+
if (!result.ok) {
|
|
1286
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1287
|
+
}
|
|
1288
|
+
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1289
|
+
return c.json(ok({
|
|
1290
|
+
...result.data,
|
|
1291
|
+
total: filteredItems.length,
|
|
1292
|
+
items: filteredItems
|
|
1293
|
+
}));
|
|
1294
|
+
});
|
|
1295
|
+
}
|
|
1296
|
+
function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
1297
|
+
app.get("/api/marketplace/skills/installed", (c) => {
|
|
1298
|
+
return c.json(ok(collectSkillMarketplaceInstalledView(options)));
|
|
1299
|
+
});
|
|
1300
|
+
app.get("/api/marketplace/skills/items", async (c) => {
|
|
1301
|
+
const query = c.req.query();
|
|
1302
|
+
const result = await fetchAllSkillMarketplaceItems({
|
|
1303
|
+
baseUrl: marketplaceBaseUrl,
|
|
1304
|
+
query: {
|
|
1305
|
+
q: query.q,
|
|
1306
|
+
tag: query.tag,
|
|
1307
|
+
sort: query.sort,
|
|
1308
|
+
page: query.page,
|
|
1309
|
+
pageSize: query.pageSize
|
|
1210
1310
|
}
|
|
1211
|
-
|
|
1212
|
-
|
|
1311
|
+
});
|
|
1312
|
+
if (!result.ok) {
|
|
1313
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1314
|
+
}
|
|
1315
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1316
|
+
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
1317
|
+
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1318
|
+
const requestedPage = toPositiveInt(query.page, 1);
|
|
1319
|
+
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
1320
|
+
const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
|
|
1321
|
+
return c.json(ok({
|
|
1322
|
+
total: filteredItems.length,
|
|
1323
|
+
page: currentPage,
|
|
1324
|
+
pageSize,
|
|
1325
|
+
totalPages,
|
|
1326
|
+
sort: result.data.sort,
|
|
1327
|
+
query: result.data.query,
|
|
1328
|
+
items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
1329
|
+
}));
|
|
1330
|
+
});
|
|
1331
|
+
app.get("/api/marketplace/skills/items/:slug", async (c) => {
|
|
1332
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1333
|
+
const result = await fetchMarketplaceData({
|
|
1334
|
+
baseUrl: marketplaceBaseUrl,
|
|
1335
|
+
path: `/api/v1/skills/items/${slug}`
|
|
1336
|
+
});
|
|
1337
|
+
if (!result.ok) {
|
|
1338
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1339
|
+
}
|
|
1340
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1341
|
+
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1342
|
+
if (!isSupportedMarketplaceSkillItem(sanitized, knownSkillNames)) {
|
|
1343
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1344
|
+
}
|
|
1345
|
+
return c.json(ok(sanitized));
|
|
1346
|
+
});
|
|
1347
|
+
app.post("/api/marketplace/skills/install", async (c) => {
|
|
1348
|
+
const body = await readJson(c.req.raw);
|
|
1349
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1350
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1351
|
+
}
|
|
1352
|
+
if (body.data.type && body.data.type !== "skill") {
|
|
1353
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1354
|
+
}
|
|
1355
|
+
try {
|
|
1356
|
+
const payload = await installMarketplaceSkill({
|
|
1357
|
+
options,
|
|
1358
|
+
body: body.data
|
|
1359
|
+
});
|
|
1360
|
+
return c.json(ok(payload));
|
|
1361
|
+
} catch (error) {
|
|
1362
|
+
const message = String(error);
|
|
1363
|
+
if (message.startsWith("INVALID_BODY:")) {
|
|
1364
|
+
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1213
1365
|
}
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
options,
|
|
1217
|
-
body: {
|
|
1218
|
-
...body.data,
|
|
1219
|
-
type: route.type
|
|
1220
|
-
}
|
|
1221
|
-
});
|
|
1222
|
-
return c.json(ok(payload));
|
|
1223
|
-
} catch (error) {
|
|
1224
|
-
const message = String(error);
|
|
1225
|
-
if (message.startsWith("INVALID_BODY:")) {
|
|
1226
|
-
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1227
|
-
}
|
|
1228
|
-
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1229
|
-
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1230
|
-
}
|
|
1231
|
-
return c.json(err("MANAGE_FAILED", message), 400);
|
|
1366
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1367
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1232
1368
|
}
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1369
|
+
return c.json(err("INSTALL_FAILED", message), 400);
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
app.post("/api/marketplace/skills/manage", async (c) => {
|
|
1373
|
+
const body = await readJson(c.req.raw);
|
|
1374
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1375
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1376
|
+
}
|
|
1377
|
+
if (body.data.type && body.data.type !== "skill") {
|
|
1378
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
const payload = await manageMarketplaceSkill({
|
|
1382
|
+
options,
|
|
1383
|
+
body: body.data
|
|
1243
1384
|
});
|
|
1244
|
-
|
|
1245
|
-
|
|
1385
|
+
return c.json(ok(payload));
|
|
1386
|
+
} catch (error) {
|
|
1387
|
+
const message = String(error);
|
|
1388
|
+
if (message.startsWith("INVALID_BODY:")) {
|
|
1389
|
+
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1390
|
+
}
|
|
1391
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1392
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1393
|
+
}
|
|
1394
|
+
return c.json(err("MANAGE_FAILED", message), 400);
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
app.get("/api/marketplace/skills/recommendations", async (c) => {
|
|
1398
|
+
const query = c.req.query();
|
|
1399
|
+
const result = await fetchMarketplaceData({
|
|
1400
|
+
baseUrl: marketplaceBaseUrl,
|
|
1401
|
+
path: "/api/v1/skills/recommendations",
|
|
1402
|
+
query: {
|
|
1403
|
+
scene: query.scene,
|
|
1404
|
+
limit: query.limit
|
|
1246
1405
|
}
|
|
1247
|
-
const knownSkillNames = collectKnownSkillNames(options);
|
|
1248
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
|
|
1249
|
-
return c.json(ok({
|
|
1250
|
-
...result.data,
|
|
1251
|
-
total: filteredItems.length,
|
|
1252
|
-
items: filteredItems
|
|
1253
|
-
}));
|
|
1254
1406
|
});
|
|
1255
|
-
|
|
1407
|
+
if (!result.ok) {
|
|
1408
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1409
|
+
}
|
|
1410
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1411
|
+
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
1412
|
+
return c.json(ok({
|
|
1413
|
+
...result.data,
|
|
1414
|
+
total: filteredItems.length,
|
|
1415
|
+
items: filteredItems
|
|
1416
|
+
}));
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1419
|
+
function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
1420
|
+
registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl);
|
|
1421
|
+
registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl);
|
|
1256
1422
|
}
|
|
1257
1423
|
function createUiRouter(options) {
|
|
1258
1424
|
const app = new Hono();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.14",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Nextclaw UI/API server.",
|
|
6
6
|
"type": "module",
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
],
|
|
16
16
|
"dependencies": {
|
|
17
17
|
"@hono/node-server": "^1.13.3",
|
|
18
|
-
"@nextclaw/openclaw-compat": "^0.1.
|
|
18
|
+
"@nextclaw/openclaw-compat": "^0.1.27",
|
|
19
19
|
"hono": "^4.6.2",
|
|
20
20
|
"ws": "^8.18.0",
|
|
21
|
-
"@nextclaw/core": "^0.6.
|
|
21
|
+
"@nextclaw/core": "^0.6.33"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^20.17.6",
|