@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 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
- pluginSpecs: string[];
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 collectMarketplaceInstalledView(options) {
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
- const pluginSpecSet = new Set(dedupedPluginRecords.map((record) => record.spec));
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 skillSpecSet = /* @__PURE__ */ new Set();
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
- total: records.length,
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 installed = collectMarketplaceInstalledView(options);
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
- app.get("/api/marketplace/installed", (c) => {
1116
- return c.json(ok(collectMarketplaceInstalledView(options)));
1117
- });
1118
- app.get("/api/marketplace/items", async (c) => {
1119
- const query = c.req.query();
1120
- const result = await fetchAllMarketplaceItems({
1121
- baseUrl: marketplaceBaseUrl,
1122
- query: {
1123
- q: query.q,
1124
- type: query.type,
1125
- tag: query.tag,
1126
- sort: query.sort,
1127
- page: query.page,
1128
- pageSize: query.pageSize
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
- if (!result.ok) {
1132
- return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
1133
- }
1134
- const knownSkillNames = collectKnownSkillNames(options);
1135
- const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
1136
- const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
1137
- const requestedPage = toPositiveInt(query.page, 1);
1138
- const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
1139
- const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
1140
- return c.json(ok({
1141
- total: filteredItems.length,
1142
- page: currentPage,
1143
- pageSize,
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
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
- if (!result.ok) {
1161
- return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
1162
- }
1163
- const knownSkillNames = collectKnownSkillNames(options);
1164
- const sanitized = sanitizeMarketplaceItem(result.data);
1165
- if (!isSupportedMarketplaceItem(sanitized, knownSkillNames)) {
1166
- return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
1167
- }
1168
- return c.json(ok(sanitized));
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();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.5.10",
3
+ "version": "0.5.11",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",