@nextclaw/server 0.5.9 → 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
@@ -1,5 +1,6 @@
1
1
  // src/ui/server.ts
2
2
  import { Hono as Hono2 } from "hono";
3
+ import { compress } from "hono/compress";
3
4
  import { cors } from "hono/cors";
4
5
  import { serve } from "@hono/node-server";
5
6
  import { WebSocketServer, WebSocket } from "ws";
@@ -800,7 +801,7 @@ async function fetchMarketplaceData(params) {
800
801
  };
801
802
  }
802
803
  }
803
- function collectMarketplaceInstalledView(options) {
804
+ function collectInstalledPluginRecords(options) {
804
805
  const config = loadConfigOrDefault(options.configPath);
805
806
  const pluginRecordsMap = config.plugins.installs ?? {};
806
807
  const pluginEntries = config.plugins.entries ?? {};
@@ -899,15 +900,22 @@ function collectMarketplaceInstalledView(options) {
899
900
  }
900
901
  }
901
902
  const dedupedPluginRecords = dedupeInstalledPluginRecordsByCanonicalSpec(pluginRecords);
902
- 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);
903
913
  const workspacePath = getWorkspacePathFromConfig3(config);
904
914
  const skillsLoader = createSkillsLoader(workspacePath);
905
915
  const availableSkillSet = new Set((skillsLoader?.listSkills(true) ?? []).map((skill) => skill.name));
906
916
  const listedSkills = skillsLoader?.listSkills(false) ?? [];
907
- const skillSpecSet = /* @__PURE__ */ new Set();
908
- const skillRecords = listedSkills.map((skill) => {
917
+ const records = listedSkills.map((skill) => {
909
918
  const enabled = availableSkillSet.has(skill.name);
910
- skillSpecSet.add(skill.name);
911
919
  return {
912
920
  type: "skill",
913
921
  id: skill.name,
@@ -917,20 +925,21 @@ function collectMarketplaceInstalledView(options) {
917
925
  enabled,
918
926
  runtimeStatus: enabled ? "enabled" : "disabled"
919
927
  };
920
- });
921
- const records = [...dedupedPluginRecords, ...skillRecords].sort((left, right) => {
922
- if (left.type !== right.type) {
923
- return left.type.localeCompare(right.type);
924
- }
925
- return left.spec.localeCompare(right.spec);
926
- });
928
+ }).sort((left, right) => left.spec.localeCompare(right.spec));
927
929
  return {
928
- total: records.length,
929
- pluginSpecs: Array.from(pluginSpecSet).sort((left, right) => left.localeCompare(right)),
930
- skillSpecs: Array.from(skillSpecSet).sort((left, right) => left.localeCompare(right)),
930
+ specs: records.map((record) => record.spec),
931
931
  records
932
932
  };
933
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
+ }
934
943
  function resolvePluginManageTargetId(options, rawTargetId, rawSpec) {
935
944
  const targetId = rawTargetId.trim();
936
945
  if (!targetId && !rawSpec) {
@@ -938,8 +947,7 @@ function resolvePluginManageTargetId(options, rawTargetId, rawSpec) {
938
947
  }
939
948
  const normalizedTarget = targetId ? normalizePluginNpmSpec(targetId).toLowerCase() : "";
940
949
  const normalizedSpec = rawSpec ? normalizePluginNpmSpec(rawSpec).toLowerCase() : "";
941
- const installed = collectMarketplaceInstalledView(options);
942
- const pluginRecords = installed.records.filter((record) => record.type === "plugin");
950
+ const pluginRecords = collectInstalledPluginRecords(options).records;
943
951
  const lowerTargetId = targetId.toLowerCase();
944
952
  for (const record of pluginRecords) {
945
953
  const recordId = record.id?.trim();
@@ -1003,6 +1011,7 @@ async function fetchAllMarketplaceItems(params) {
1003
1011
  path: "/api/v1/items",
1004
1012
  query: {
1005
1013
  ...params.query,
1014
+ type: params.type,
1006
1015
  page: String(remotePage),
1007
1016
  pageSize: String(MARKETPLACE_REMOTE_PAGE_SIZE)
1008
1017
  }
@@ -1111,61 +1120,122 @@ async function manageMarketplaceItem(params) {
1111
1120
  };
1112
1121
  }
1113
1122
  function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
1114
- app.get("/api/marketplace/installed", (c) => {
1115
- return c.json(ok(collectMarketplaceInstalledView(options)));
1116
- });
1117
- app.get("/api/marketplace/items", async (c) => {
1118
- const query = c.req.query();
1119
- const result = await fetchAllMarketplaceItems({
1120
- baseUrl: marketplaceBaseUrl,
1121
- query: {
1122
- q: query.q,
1123
- type: query.type,
1124
- tag: query.tag,
1125
- sort: query.sort,
1126
- page: query.page,
1127
- 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);
1128
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
+ }));
1129
1162
  });
1130
- if (!result.ok) {
1131
- return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
1132
- }
1133
- const knownSkillNames = collectKnownSkillNames(options);
1134
- const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceItem(item, knownSkillNames));
1135
- const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
1136
- const requestedPage = toPositiveInt(query.page, 1);
1137
- const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
1138
- const currentPage = totalPages === 0 ? 1 : Math.min(requestedPage, totalPages);
1139
- return c.json(ok({
1140
- total: filteredItems.length,
1141
- page: currentPage,
1142
- pageSize,
1143
- totalPages,
1144
- sort: result.data.sort,
1145
- query: result.data.query,
1146
- items: filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize)
1147
- }));
1148
- });
1149
- app.get("/api/marketplace/items/:slug", async (c) => {
1150
- const slug = encodeURIComponent(c.req.param("slug"));
1151
- const type = c.req.query("type");
1152
- const result = await fetchMarketplaceData({
1153
- baseUrl: marketplaceBaseUrl,
1154
- path: `/api/v1/items/${slug}`,
1155
- query: {
1156
- 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);
1157
1179
  }
1180
+ return c.json(ok(sanitized));
1158
1181
  });
1159
- if (!result.ok) {
1160
- return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
1161
- }
1162
- const knownSkillNames = collectKnownSkillNames(options);
1163
- const sanitized = sanitizeMarketplaceItem(result.data);
1164
- if (!isSupportedMarketplaceItem(sanitized, knownSkillNames)) {
1165
- return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
1166
- }
1167
- return c.json(ok(sanitized));
1168
- });
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
+ }
1169
1239
  app.get("/api/marketplace/recommendations", async (c) => {
1170
1240
  const query = c.req.query();
1171
1241
  const result = await fetchMarketplaceData({
@@ -1187,44 +1257,6 @@ function registerMarketplaceRoutes(app, options, marketplaceBaseUrl) {
1187
1257
  items: filteredItems
1188
1258
  }));
1189
1259
  });
1190
- app.post("/api/marketplace/install", async (c) => {
1191
- const body = await readJson(c.req.raw);
1192
- if (!body.ok || !body.data || typeof body.data !== "object") {
1193
- return c.json(err("INVALID_BODY", "invalid json body"), 400);
1194
- }
1195
- try {
1196
- const payload = await installMarketplaceItem({ options, body: body.data });
1197
- return c.json(ok(payload));
1198
- } catch (error) {
1199
- const message = String(error);
1200
- if (message.startsWith("INVALID_BODY:")) {
1201
- return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
1202
- }
1203
- if (message.startsWith("NOT_AVAILABLE:")) {
1204
- return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
1205
- }
1206
- return c.json(err("INSTALL_FAILED", message), 400);
1207
- }
1208
- });
1209
- app.post("/api/marketplace/manage", async (c) => {
1210
- const body = await readJson(c.req.raw);
1211
- if (!body.ok || !body.data || typeof body.data !== "object") {
1212
- return c.json(err("INVALID_BODY", "invalid json body"), 400);
1213
- }
1214
- try {
1215
- const payload = await manageMarketplaceItem({ options, body: body.data });
1216
- return c.json(ok(payload));
1217
- } catch (error) {
1218
- const message = String(error);
1219
- if (message.startsWith("INVALID_BODY:")) {
1220
- return c.json(err("INVALID_BODY", message.slice("INVALID_BODY:".length)), 400);
1221
- }
1222
- if (message.startsWith("NOT_AVAILABLE:")) {
1223
- return c.json(err("NOT_AVAILABLE", message.slice("NOT_AVAILABLE:".length)), 503);
1224
- }
1225
- return c.json(err("MANAGE_FAILED", message), 400);
1226
- }
1227
- });
1228
1260
  }
1229
1261
  function createUiRouter(options) {
1230
1262
  const app = new Hono();
@@ -1441,6 +1473,7 @@ var DEFAULT_CORS_ORIGINS = (origin) => {
1441
1473
  };
1442
1474
  function startUiServer(options) {
1443
1475
  const app = new Hono2();
1476
+ app.use("/*", compress());
1444
1477
  const origin = options.corsOrigins ?? DEFAULT_CORS_ORIGINS;
1445
1478
  app.use("/api/*", cors({ origin }));
1446
1479
  const clients = /* @__PURE__ */ new Set();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/server",
3
- "version": "0.5.9",
3
+ "version": "0.5.11",
4
4
  "private": false,
5
5
  "description": "Nextclaw UI/API server.",
6
6
  "type": "module",