@nextclaw/server 0.5.10 → 0.5.12
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 +3 -2
- package/dist/index.js +149 -122
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -299,6 +299,7 @@ type MarketplaceListView = {
|
|
|
299
299
|
items: MarketplaceItemSummary[];
|
|
300
300
|
};
|
|
301
301
|
type MarketplaceRecommendationView = {
|
|
302
|
+
type: MarketplaceItemType;
|
|
302
303
|
sceneId: string;
|
|
303
304
|
title: string;
|
|
304
305
|
description?: string;
|
|
@@ -318,9 +319,9 @@ type MarketplaceInstalledRecord = {
|
|
|
318
319
|
installPath?: string;
|
|
319
320
|
};
|
|
320
321
|
type MarketplaceInstalledView = {
|
|
322
|
+
type: MarketplaceItemType;
|
|
321
323
|
total: number;
|
|
322
|
-
|
|
323
|
-
skillSpecs: string[];
|
|
324
|
+
specs: string[];
|
|
324
325
|
records: MarketplaceInstalledRecord[];
|
|
325
326
|
};
|
|
326
327
|
type MarketplaceInstallRequest = {
|
package/dist/index.js
CHANGED
|
@@ -801,7 +801,7 @@ async function fetchMarketplaceData(params) {
|
|
|
801
801
|
};
|
|
802
802
|
}
|
|
803
803
|
}
|
|
804
|
-
function
|
|
804
|
+
function collectInstalledPluginRecords(options) {
|
|
805
805
|
const config = loadConfigOrDefault(options.configPath);
|
|
806
806
|
const pluginRecordsMap = config.plugins.installs ?? {};
|
|
807
807
|
const pluginEntries = config.plugins.entries ?? {};
|
|
@@ -900,15 +900,22 @@ function collectMarketplaceInstalledView(options) {
|
|
|
900
900
|
}
|
|
901
901
|
}
|
|
902
902
|
const dedupedPluginRecords = dedupeInstalledPluginRecordsByCanonicalSpec(pluginRecords);
|
|
903
|
-
|
|
903
|
+
dedupedPluginRecords.sort((left, right) => {
|
|
904
|
+
return left.spec.localeCompare(right.spec);
|
|
905
|
+
});
|
|
906
|
+
return {
|
|
907
|
+
specs: dedupedPluginRecords.map((record) => record.spec),
|
|
908
|
+
records: dedupedPluginRecords
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function collectInstalledSkillRecords(options) {
|
|
912
|
+
const config = loadConfigOrDefault(options.configPath);
|
|
904
913
|
const workspacePath = getWorkspacePathFromConfig3(config);
|
|
905
914
|
const skillsLoader = createSkillsLoader(workspacePath);
|
|
906
915
|
const availableSkillSet = new Set((skillsLoader?.listSkills(true) ?? []).map((skill) => skill.name));
|
|
907
916
|
const listedSkills = skillsLoader?.listSkills(false) ?? [];
|
|
908
|
-
const
|
|
909
|
-
const skillRecords = listedSkills.map((skill) => {
|
|
917
|
+
const records = listedSkills.map((skill) => {
|
|
910
918
|
const enabled = availableSkillSet.has(skill.name);
|
|
911
|
-
skillSpecSet.add(skill.name);
|
|
912
919
|
return {
|
|
913
920
|
type: "skill",
|
|
914
921
|
id: skill.name,
|
|
@@ -918,20 +925,21 @@ function collectMarketplaceInstalledView(options) {
|
|
|
918
925
|
enabled,
|
|
919
926
|
runtimeStatus: enabled ? "enabled" : "disabled"
|
|
920
927
|
};
|
|
921
|
-
});
|
|
922
|
-
const records = [...dedupedPluginRecords, ...skillRecords].sort((left, right) => {
|
|
923
|
-
if (left.type !== right.type) {
|
|
924
|
-
return left.type.localeCompare(right.type);
|
|
925
|
-
}
|
|
926
|
-
return left.spec.localeCompare(right.spec);
|
|
927
|
-
});
|
|
928
|
+
}).sort((left, right) => left.spec.localeCompare(right.spec));
|
|
928
929
|
return {
|
|
929
|
-
|
|
930
|
-
pluginSpecs: Array.from(pluginSpecSet).sort((left, right) => left.localeCompare(right)),
|
|
931
|
-
skillSpecs: Array.from(skillSpecSet).sort((left, right) => left.localeCompare(right)),
|
|
930
|
+
specs: records.map((record) => record.spec),
|
|
932
931
|
records
|
|
933
932
|
};
|
|
934
933
|
}
|
|
934
|
+
function collectMarketplaceInstalledView(options, type) {
|
|
935
|
+
const installed = type === "plugin" ? collectInstalledPluginRecords(options) : collectInstalledSkillRecords(options);
|
|
936
|
+
return {
|
|
937
|
+
type,
|
|
938
|
+
total: installed.records.length,
|
|
939
|
+
specs: installed.specs,
|
|
940
|
+
records: installed.records
|
|
941
|
+
};
|
|
942
|
+
}
|
|
935
943
|
function resolvePluginManageTargetId(options, rawTargetId, rawSpec) {
|
|
936
944
|
const targetId = rawTargetId.trim();
|
|
937
945
|
if (!targetId && !rawSpec) {
|
|
@@ -939,8 +947,7 @@ function resolvePluginManageTargetId(options, rawTargetId, rawSpec) {
|
|
|
939
947
|
}
|
|
940
948
|
const normalizedTarget = targetId ? normalizePluginNpmSpec(targetId).toLowerCase() : "";
|
|
941
949
|
const normalizedSpec = rawSpec ? normalizePluginNpmSpec(rawSpec).toLowerCase() : "";
|
|
942
|
-
const
|
|
943
|
-
const pluginRecords = installed.records.filter((record) => record.type === "plugin");
|
|
950
|
+
const pluginRecords = collectInstalledPluginRecords(options).records;
|
|
944
951
|
const lowerTargetId = targetId.toLowerCase();
|
|
945
952
|
for (const record of pluginRecords) {
|
|
946
953
|
const recordId = record.id?.trim();
|
|
@@ -1001,7 +1008,7 @@ async function fetchAllMarketplaceItems(params) {
|
|
|
1001
1008
|
while (remotePage <= remoteTotalPages && remotePage <= MARKETPLACE_REMOTE_MAX_PAGES) {
|
|
1002
1009
|
const result = await fetchMarketplaceData({
|
|
1003
1010
|
baseUrl: params.baseUrl,
|
|
1004
|
-
path:
|
|
1011
|
+
path: `/api/v1/${params.segment}/items`,
|
|
1005
1012
|
query: {
|
|
1006
1013
|
...params.query,
|
|
1007
1014
|
page: String(remotePage),
|
|
@@ -1112,120 +1119,140 @@ async function manageMarketplaceItem(params) {
|
|
|
1112
1119
|
};
|
|
1113
1120
|
}
|
|
1114
1121
|
function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1122
|
+
const typedMarketplaceRoutes = [
|
|
1123
|
+
{ segment: "plugins", type: "plugin" },
|
|
1124
|
+
{ segment: "skills", type: "skill" }
|
|
1125
|
+
];
|
|
1126
|
+
for (const route of typedMarketplaceRoutes) {
|
|
1127
|
+
app.get(`/api/marketplace/${route.segment}/installed`, (c) => {
|
|
1128
|
+
return c.json(ok(collectMarketplaceInstalledView(options, route.type)));
|
|
1129
|
+
});
|
|
1130
|
+
app.get(`/api/marketplace/${route.segment}/items`, async (c) => {
|
|
1131
|
+
const query = c.req.query();
|
|
1132
|
+
const result = await fetchAllMarketplaceItems({
|
|
1133
|
+
baseUrl: marketplaceBaseUrl,
|
|
1134
|
+
segment: route.segment,
|
|
1135
|
+
query: {
|
|
1136
|
+
q: query.q,
|
|
1137
|
+
tag: query.tag,
|
|
1138
|
+
sort: query.sort,
|
|
1139
|
+
page: query.page,
|
|
1140
|
+
pageSize: query.pageSize
|
|
1141
|
+
}
|
|
1142
|
+
});
|
|
1143
|
+
if (!result.ok) {
|
|
1144
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1129
1145
|
}
|
|
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
|
+
}));
|
|
1130
1161
|
});
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
totalPages,
|
|
1145
|
-
sort: result.data.sort,
|
|
1146
|
-
query: result.data.query,
|
|
1147
|
-
items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
1148
|
-
}));
|
|
1149
|
-
});
|
|
1150
|
-
app.get("/api/marketplace/items/:slug", async (c) => {
|
|
1151
|
-
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1152
|
-
const type = c.req.query("type");
|
|
1153
|
-
const result = await fetchMarketplaceData({
|
|
1154
|
-
baseUrl: marketplaceBaseUrl,
|
|
1155
|
-
path: `/api/v1/items/${slug}`,
|
|
1156
|
-
query: {
|
|
1157
|
-
type
|
|
1162
|
+
app.get(`/api/marketplace/${route.segment}/items/:slug`, async (c) => {
|
|
1163
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1164
|
+
const result = await fetchMarketplaceData({
|
|
1165
|
+
baseUrl: marketplaceBaseUrl,
|
|
1166
|
+
path: `/api/v1/${route.segment}/items/${slug}`
|
|
1167
|
+
});
|
|
1168
|
+
if (!result.ok) {
|
|
1169
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1170
|
+
}
|
|
1171
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1172
|
+
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1173
|
+
if (!isSupportedMarketplaceItem(sanitized, knownSkillNames)) {
|
|
1174
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1158
1175
|
}
|
|
1176
|
+
return c.json(ok(sanitized));
|
|
1159
1177
|
});
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
+
app.post(`/api/marketplace/${route.segment}/install`, async (c) => {
|
|
1179
|
+
const body = await readJson(c.req.raw);
|
|
1180
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1181
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1182
|
+
}
|
|
1183
|
+
if (body.data.type && body.data.type !== route.type) {
|
|
1184
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1185
|
+
}
|
|
1186
|
+
try {
|
|
1187
|
+
const payload = await installMarketplaceItem({
|
|
1188
|
+
options,
|
|
1189
|
+
body: {
|
|
1190
|
+
...body.data,
|
|
1191
|
+
type: route.type
|
|
1192
|
+
}
|
|
1193
|
+
});
|
|
1194
|
+
return c.json(ok(payload));
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
const message = String(error);
|
|
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);
|
|
1178
1204
|
}
|
|
1179
1205
|
});
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
|
|
1185
|
-
return c.json(ok({
|
|
1186
|
-
...result.data,
|
|
1187
|
-
total: filteredItems.length,
|
|
1188
|
-
items: filteredItems
|
|
1189
|
-
}));
|
|
1190
|
-
});
|
|
1191
|
-
app.post("/api/marketplace/install", async (c) => {
|
|
1192
|
-
const body = await readJson(c.req.raw);
|
|
1193
|
-
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1194
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1195
|
-
}
|
|
1196
|
-
try {
|
|
1197
|
-
const payload = await installMarketplaceItem({ options, body: body.data });
|
|
1198
|
-
return c.json(ok(payload));
|
|
1199
|
-
} catch (error) {
|
|
1200
|
-
const message = String(error);
|
|
1201
|
-
if (message.startsWith("INVALID_BODY:")) {
|
|
1202
|
-
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1206
|
+
app.post(`/api/marketplace/${route.segment}/manage`, async (c) => {
|
|
1207
|
+
const body = await readJson(c.req.raw);
|
|
1208
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1209
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1203
1210
|
}
|
|
1204
|
-
if (
|
|
1205
|
-
return c.json(err("
|
|
1211
|
+
if (body.data.type && body.data.type !== route.type) {
|
|
1212
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1206
1213
|
}
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1214
|
+
try {
|
|
1215
|
+
const payload = await manageMarketplaceItem({
|
|
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);
|
|
1222
1232
|
}
|
|
1223
|
-
|
|
1224
|
-
|
|
1233
|
+
});
|
|
1234
|
+
app.get(`/api/marketplace/${route.segment}/recommendations`, async (c) => {
|
|
1235
|
+
const query = c.req.query();
|
|
1236
|
+
const result = await fetchMarketplaceData({
|
|
1237
|
+
baseUrl: marketplaceBaseUrl,
|
|
1238
|
+
path: `/api/v1/${route.segment}/recommendations`,
|
|
1239
|
+
query: {
|
|
1240
|
+
scene: query.scene,
|
|
1241
|
+
limit: query.limit
|
|
1242
|
+
}
|
|
1243
|
+
});
|
|
1244
|
+
if (!result.ok) {
|
|
1245
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1225
1246
|
}
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
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
|
+
});
|
|
1255
|
+
}
|
|
1229
1256
|
}
|
|
1230
1257
|
function createUiRouter(options) {
|
|
1231
1258
|
const app = new Hono();
|