@nextclaw/server 0.5.10 → 0.5.11
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 +2 -2
- package/dist/index.js +137 -106
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -318,9 +318,9 @@ type MarketplaceInstalledRecord = {
|
|
|
318
318
|
installPath?: string;
|
|
319
319
|
};
|
|
320
320
|
type MarketplaceInstalledView = {
|
|
321
|
+
type: MarketplaceItemType;
|
|
321
322
|
total: number;
|
|
322
|
-
|
|
323
|
-
skillSpecs: string[];
|
|
323
|
+
specs: string[];
|
|
324
324
|
records: MarketplaceInstalledRecord[];
|
|
325
325
|
};
|
|
326
326
|
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();
|
|
@@ -1004,6 +1011,7 @@ async function fetchAllMarketplaceItems(params) {
|
|
|
1004
1011
|
path: "/api/v1/items",
|
|
1005
1012
|
query: {
|
|
1006
1013
|
...params.query,
|
|
1014
|
+
type: params.type,
|
|
1007
1015
|
page: String(remotePage),
|
|
1008
1016
|
pageSize: String(MARKETPLACE_REMOTE_PAGE_SIZE)
|
|
1009
1017
|
}
|
|
@@ -1112,61 +1120,122 @@ async function manageMarketplaceItem(params) {
|
|
|
1112
1120
|
};
|
|
1113
1121
|
}
|
|
1114
1122
|
function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1123
|
+
const typedMarketplaceRoutes = [
|
|
1124
|
+
{ segment: "plugins", type: "plugin" },
|
|
1125
|
+
{ segment: "skills", type: "skill" }
|
|
1126
|
+
];
|
|
1127
|
+
for (const route of typedMarketplaceRoutes) {
|
|
1128
|
+
app.get(`/api/marketplace/${route.segment}/installed`, (c) => {
|
|
1129
|
+
return c.json(ok(collectMarketplaceInstalledView(options, route.type)));
|
|
1130
|
+
});
|
|
1131
|
+
app.get(`/api/marketplace/${route.segment}/items`, async (c) => {
|
|
1132
|
+
const query = c.req.query();
|
|
1133
|
+
const result = await fetchAllMarketplaceItems({
|
|
1134
|
+
baseUrl: marketplaceBaseUrl,
|
|
1135
|
+
type: route.type,
|
|
1136
|
+
query: {
|
|
1137
|
+
q: query.q,
|
|
1138
|
+
tag: query.tag,
|
|
1139
|
+
sort: query.sort,
|
|
1140
|
+
page: query.page,
|
|
1141
|
+
pageSize: query.pageSize
|
|
1142
|
+
}
|
|
1143
|
+
});
|
|
1144
|
+
if (!result.ok) {
|
|
1145
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1129
1146
|
}
|
|
1147
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1148
|
+
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
|
|
1149
|
+
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1150
|
+
const requestedPage = toPositiveInt(query.page, 1);
|
|
1151
|
+
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
1152
|
+
const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
|
|
1153
|
+
return c.json(ok({
|
|
1154
|
+
total: filteredItems.length,
|
|
1155
|
+
page: currentPage,
|
|
1156
|
+
pageSize,
|
|
1157
|
+
totalPages,
|
|
1158
|
+
sort: result.data.sort,
|
|
1159
|
+
query: result.data.query,
|
|
1160
|
+
items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
|
|
1161
|
+
}));
|
|
1130
1162
|
});
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
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
|
|
1163
|
+
app.get(`/api/marketplace/${route.segment}/items/:slug`, async (c) => {
|
|
1164
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1165
|
+
const result = await fetchMarketplaceData({
|
|
1166
|
+
baseUrl: marketplaceBaseUrl,
|
|
1167
|
+
path: `/api/v1/items/${slug}`,
|
|
1168
|
+
query: {
|
|
1169
|
+
type: route.type
|
|
1170
|
+
}
|
|
1171
|
+
});
|
|
1172
|
+
if (!result.ok) {
|
|
1173
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1174
|
+
}
|
|
1175
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1176
|
+
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1177
|
+
if (!isSupportedMarketplaceItem(sanitized, knownSkillNames)) {
|
|
1178
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1158
1179
|
}
|
|
1180
|
+
return c.json(ok(sanitized));
|
|
1159
1181
|
});
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1182
|
+
app.post(`/api/marketplace/${route.segment}/install`, async (c) => {
|
|
1183
|
+
const body = await readJson(c.req.raw);
|
|
1184
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1185
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1186
|
+
}
|
|
1187
|
+
if (body.data.type && body.data.type !== route.type) {
|
|
1188
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1189
|
+
}
|
|
1190
|
+
try {
|
|
1191
|
+
const payload = await installMarketplaceItem({
|
|
1192
|
+
options,
|
|
1193
|
+
body: {
|
|
1194
|
+
...body.data,
|
|
1195
|
+
type: route.type
|
|
1196
|
+
}
|
|
1197
|
+
});
|
|
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);
|
|
1203
|
+
}
|
|
1204
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1205
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1206
|
+
}
|
|
1207
|
+
return c.json(err("INSTALL_FAILED", message), 400);
|
|
1208
|
+
}
|
|
1209
|
+
});
|
|
1210
|
+
app.post(`/api/marketplace/${route.segment}/manage`, async (c) => {
|
|
1211
|
+
const body = await readJson(c.req.raw);
|
|
1212
|
+
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1213
|
+
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1214
|
+
}
|
|
1215
|
+
if (body.data.type && body.data.type !== route.type) {
|
|
1216
|
+
return c.json(err("INVALID_BODY", "body.type does not match route type"), 400);
|
|
1217
|
+
}
|
|
1218
|
+
try {
|
|
1219
|
+
const payload = await manageMarketplaceItem({
|
|
1220
|
+
options,
|
|
1221
|
+
body: {
|
|
1222
|
+
...body.data,
|
|
1223
|
+
type: route.type
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
return c.json(ok(payload));
|
|
1227
|
+
} catch (error) {
|
|
1228
|
+
const message = String(error);
|
|
1229
|
+
if (message.startsWith("INVALID_BODY:")) {
|
|
1230
|
+
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1231
|
+
}
|
|
1232
|
+
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1233
|
+
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1234
|
+
}
|
|
1235
|
+
return c.json(err("MANAGE_FAILED", message), 400);
|
|
1236
|
+
}
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1170
1239
|
app.get("/api/marketplace/recommendations", async (c) => {
|
|
1171
1240
|
const query = c.req.query();
|
|
1172
1241
|
const result = await fetchMarketplaceData({
|
|
@@ -1188,44 +1257,6 @@ function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1188
1257
|
items: filteredItems
|
|
1189
1258
|
}));
|
|
1190
1259
|
});
|
|
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);
|
|
1203
|
-
}
|
|
1204
|
-
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1205
|
-
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1206
|
-
}
|
|
1207
|
-
return c.json(err("INSTALL_FAILED", message), 400);
|
|
1208
|
-
}
|
|
1209
|
-
});
|
|
1210
|
-
app.post("/api/marketplace/manage", async (c) => {
|
|
1211
|
-
const body = await readJson(c.req.raw);
|
|
1212
|
-
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
1213
|
-
return c.json(err("INVALID_BODY", "invalid json body"), 400);
|
|
1214
|
-
}
|
|
1215
|
-
try {
|
|
1216
|
-
const payload = await manageMarketplaceItem({ options, body: body.data });
|
|
1217
|
-
return c.json(ok(payload));
|
|
1218
|
-
} catch (error) {
|
|
1219
|
-
const message = String(error);
|
|
1220
|
-
if (message.startsWith("INVALID_BODY:")) {
|
|
1221
|
-
return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
|
|
1222
|
-
}
|
|
1223
|
-
if (message.startsWith("NOT_AVAILABLE:")) {
|
|
1224
|
-
return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
|
|
1225
|
-
}
|
|
1226
|
-
return c.json(err("MANAGE_FAILED", message), 400);
|
|
1227
|
-
}
|
|
1228
|
-
});
|
|
1229
1260
|
}
|
|
1230
1261
|
function createUiRouter(options) {
|
|
1231
1262
|
const app = new Hono();
|