@nextclaw/server 0.5.25 → 0.5.26
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 +26 -1
- package/dist/index.js +367 -9
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -387,12 +387,14 @@ type MarketplaceInstallSpec = {
|
|
|
387
387
|
spec: string;
|
|
388
388
|
command: string;
|
|
389
389
|
};
|
|
390
|
+
type MarketplaceLocalizedTextMap = Record<string, string>;
|
|
390
391
|
type MarketplaceItemSummary = {
|
|
391
392
|
id: string;
|
|
392
393
|
slug: string;
|
|
393
394
|
type: MarketplaceItemType;
|
|
394
395
|
name: string;
|
|
395
396
|
summary: string;
|
|
397
|
+
summaryI18n: MarketplaceLocalizedTextMap;
|
|
396
398
|
tags: string[];
|
|
397
399
|
author: string;
|
|
398
400
|
install: MarketplaceInstallSpec;
|
|
@@ -400,10 +402,33 @@ type MarketplaceItemSummary = {
|
|
|
400
402
|
};
|
|
401
403
|
type MarketplaceItemView = MarketplaceItemSummary & {
|
|
402
404
|
description?: string;
|
|
405
|
+
descriptionI18n?: MarketplaceLocalizedTextMap;
|
|
403
406
|
sourceRepo?: string;
|
|
404
407
|
homepage?: string;
|
|
405
408
|
publishedAt: string;
|
|
406
409
|
};
|
|
410
|
+
type MarketplaceSkillContentView = {
|
|
411
|
+
type: "skill";
|
|
412
|
+
slug: string;
|
|
413
|
+
name: string;
|
|
414
|
+
install: MarketplaceInstallSpec;
|
|
415
|
+
source: "workspace" | "builtin" | "git" | "remote";
|
|
416
|
+
raw: string;
|
|
417
|
+
metadataRaw?: string;
|
|
418
|
+
bodyRaw: string;
|
|
419
|
+
sourceUrl?: string;
|
|
420
|
+
};
|
|
421
|
+
type MarketplacePluginContentView = {
|
|
422
|
+
type: "plugin";
|
|
423
|
+
slug: string;
|
|
424
|
+
name: string;
|
|
425
|
+
install: MarketplaceInstallSpec;
|
|
426
|
+
source: "npm" | "repo" | "remote";
|
|
427
|
+
raw?: string;
|
|
428
|
+
bodyRaw?: string;
|
|
429
|
+
metadataRaw?: string;
|
|
430
|
+
sourceUrl?: string;
|
|
431
|
+
};
|
|
407
432
|
type MarketplaceListView = {
|
|
408
433
|
total: number;
|
|
409
434
|
page: number;
|
|
@@ -610,4 +635,4 @@ declare function deleteSession(configPath: string, key: string): boolean;
|
|
|
610
635
|
declare function updateRuntime(configPath: string, patch: RuntimeConfigUpdate): Pick<ConfigView, "agents" | "bindings" | "session">;
|
|
611
636
|
declare function updateSecrets(configPath: string, patch: SecretsConfigUpdate): SecretsView;
|
|
612
637
|
|
|
613
|
-
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStreamEvent, type ChatTurnView, 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 ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderSpecView, type RuntimeConfigUpdate, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSecrets };
|
|
638
|
+
export { type AgentBindingView, type AgentProfileView, type ApiError, type ApiResponse, type BindingPeerView, type ChannelSpecView, type ChatTurnRequest, type ChatTurnResult, type ChatTurnStreamEvent, type ChatTurnView, 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 MarketplaceLocalizedTextMap, type MarketplacePluginContentView, type MarketplacePluginInstallRequest, type MarketplacePluginInstallResult, type MarketplacePluginManageAction, type MarketplacePluginManageRequest, type MarketplacePluginManageResult, type MarketplaceRecommendationView, type MarketplaceSkillContentView, type MarketplaceSkillInstallRequest, type MarketplaceSkillInstallResult, type MarketplaceSkillManageAction, type MarketplaceSkillManageRequest, type MarketplaceSkillManageResult, type MarketplaceSort, type ProviderConfigUpdate, type ProviderConfigView, type ProviderConnectionTestRequest, type ProviderConnectionTestResult, type ProviderSpecView, type RuntimeConfigUpdate, type SecretProviderEnvView, type SecretProviderExecView, type SecretProviderFileView, type SecretProviderView, type SecretRefView, type SecretSourceView, type SecretsConfigUpdate, type SecretsView, type SessionConfigView, type SessionEntryView, type SessionEventView, type SessionHistoryView, type SessionMessageView, type SessionPatchUpdate, type SessionsListView, type UiChatRuntime, type UiServerEvent, type UiServerHandle, type UiServerOptions, buildConfigMeta, buildConfigSchemaView, buildConfigView, createUiRouter, deleteSession, executeConfigAction, getSessionHistory, listSessions, loadConfigOrDefault, patchSession, startUiServer, testProviderConnection, updateChannel, updateModel, updateProvider, updateRuntime, updateSecrets };
|
package/dist/index.js
CHANGED
|
@@ -5,11 +5,12 @@ import { cors } from "hono/cors";
|
|
|
5
5
|
import { serve } from "@hono/node-server";
|
|
6
6
|
import { WebSocketServer, WebSocket } from "ws";
|
|
7
7
|
import { existsSync, readFileSync } from "fs";
|
|
8
|
-
import { readFile, stat } from "fs/promises";
|
|
8
|
+
import { readFile as readFile2, stat } from "fs/promises";
|
|
9
9
|
import { join } from "path";
|
|
10
10
|
|
|
11
11
|
// src/ui/router.ts
|
|
12
12
|
import { Hono } from "hono";
|
|
13
|
+
import { readFile } from "fs/promises";
|
|
13
14
|
import * as NextclawCore from "@nextclaw/core";
|
|
14
15
|
import { buildPluginStatusReport } from "@nextclaw/openclaw-compat";
|
|
15
16
|
|
|
@@ -34,7 +35,7 @@ var MASK_MIN_LENGTH = 8;
|
|
|
34
35
|
var EXTRA_SENSITIVE_PATH_PATTERNS = [/authorization/i, /cookie/i, /session/i, /bearer/i];
|
|
35
36
|
var PROVIDER_TEST_MODEL_FALLBACKS = {
|
|
36
37
|
openai: "gpt-5-mini",
|
|
37
|
-
deepseek: "deepseek-
|
|
38
|
+
deepseek: "deepseek-chat",
|
|
38
39
|
gemini: "gemini-3-flash-preview",
|
|
39
40
|
zhipu: "glm-5",
|
|
40
41
|
dashscope: "qwen3.5-flash",
|
|
@@ -1287,6 +1288,141 @@ function sanitizeMarketplaceItem(item) {
|
|
|
1287
1288
|
delete next.metrics;
|
|
1288
1289
|
return next;
|
|
1289
1290
|
}
|
|
1291
|
+
var MARKETPLACE_ZH_COPY_BY_SLUG = {
|
|
1292
|
+
weather: {
|
|
1293
|
+
summary: "NextClaw \u5185\u7F6E\u6280\u80FD\uFF0C\u7528\u4E8E\u5929\u6C14\u67E5\u8BE2\u5DE5\u4F5C\u6D41\u3002",
|
|
1294
|
+
description: "\u5728 NextClaw \u4E2D\u63D0\u4F9B\u5FEB\u901F\u5929\u6C14\u67E5\u8BE2\u5DE5\u4F5C\u6D41\u3002"
|
|
1295
|
+
},
|
|
1296
|
+
summarize: {
|
|
1297
|
+
summary: "NextClaw \u5185\u7F6E\u6280\u80FD\uFF0C\u7528\u4E8E\u7ED3\u6784\u5316\u6458\u8981\u3002",
|
|
1298
|
+
description: "\u5728 NextClaw \u4E2D\u63D0\u4F9B\u6587\u4EF6\u4E0E\u957F\u6587\u672C\u7684\u6458\u8981\u5DE5\u4F5C\u6D41\u3002"
|
|
1299
|
+
},
|
|
1300
|
+
github: {
|
|
1301
|
+
summary: "NextClaw \u5185\u7F6E\u6280\u80FD\uFF0C\u7528\u4E8E GitHub \u5DE5\u4F5C\u6D41\u3002",
|
|
1302
|
+
description: "\u5728 NextClaw \u4E2D\u63D0\u4F9B Issue\u3001PR \u4E0E\u4ED3\u5E93\u76F8\u5173\u5DE5\u4F5C\u6D41\u6307\u5F15\u3002"
|
|
1303
|
+
},
|
|
1304
|
+
tmux: {
|
|
1305
|
+
summary: "NextClaw \u5185\u7F6E\u6280\u80FD\uFF0C\u7528\u4E8E\u7EC8\u7AEF/Tmux \u534F\u4F5C\u5DE5\u4F5C\u6D41\u3002",
|
|
1306
|
+
description: "\u5728 NextClaw \u4E2D\u63D0\u4F9B\u57FA\u4E8E Tmux \u7684\u4EFB\u52A1\u6267\u884C\u5DE5\u4F5C\u6D41\u6307\u5F15\u3002"
|
|
1307
|
+
},
|
|
1308
|
+
gog: {
|
|
1309
|
+
summary: "NextClaw \u5185\u7F6E\u6280\u80FD\uFF0C\u7528\u4E8E\u56FE\u8C31\u5BFC\u5411\u751F\u6210\u5DE5\u4F5C\u6D41\u3002",
|
|
1310
|
+
description: "\u5728 NextClaw \u4E2D\u63D0\u4F9B\u56FE\u8C31\u4E0E\u89C4\u5212\u5BFC\u5411\u5DE5\u4F5C\u6D41\u6307\u5F15\u3002"
|
|
1311
|
+
},
|
|
1312
|
+
pdf: {
|
|
1313
|
+
summary: "Anthropic \u6280\u80FD\uFF0C\u7528\u4E8E PDF \u8BFB\u53D6/\u5408\u5E76/\u62C6\u5206/OCR \u5DE5\u4F5C\u6D41\u3002",
|
|
1314
|
+
description: "\u4F7F\u7528\u8BE5\u6280\u80FD\u53EF\u8BFB\u53D6\u3001\u63D0\u53D6\u3001\u5408\u5E76\u3001\u62C6\u5206\u3001\u65CB\u8F6C\u5E76\u5BF9 PDF \u6267\u884C OCR \u5904\u7406\u3002"
|
|
1315
|
+
},
|
|
1316
|
+
docx: {
|
|
1317
|
+
summary: "Anthropic \u6280\u80FD\uFF0C\u7528\u4E8E\u521B\u5EFA\u548C\u7F16\u8F91 Word \u6587\u6863\u3002",
|
|
1318
|
+
description: "\u4F7F\u7528\u8BE5\u6280\u80FD\u53EF\u521B\u5EFA\u3001\u8BFB\u53D6\u3001\u7F16\u8F91\u5E76\u91CD\u6784 .docx \u6587\u6863\u3002"
|
|
1319
|
+
},
|
|
1320
|
+
pptx: {
|
|
1321
|
+
summary: "Anthropic \u6280\u80FD\uFF0C\u7528\u4E8E\u6F14\u793A\u6587\u7A3F\u64CD\u4F5C\u3002",
|
|
1322
|
+
description: "\u4F7F\u7528\u8BE5\u6280\u80FD\u53EF\u521B\u5EFA\u3001\u89E3\u6790\u3001\u7F16\u8F91\u5E76\u91CD\u7EC4 .pptx \u6F14\u793A\u6587\u7A3F\u3002"
|
|
1323
|
+
},
|
|
1324
|
+
xlsx: {
|
|
1325
|
+
summary: "Anthropic \u6280\u80FD\uFF0C\u7528\u4E8E\u8868\u683C\u6587\u6863\u5DE5\u4F5C\u6D41\u3002",
|
|
1326
|
+
description: "\u4F7F\u7528\u8BE5\u6280\u80FD\u53EF\u6253\u5F00\u3001\u7F16\u8F91\u3001\u6E05\u6D17\u5E76\u8F6C\u6362 .xlsx \u4E0E .csv \u7B49\u8868\u683C\u6587\u4EF6\u3002"
|
|
1327
|
+
},
|
|
1328
|
+
bird: {
|
|
1329
|
+
summary: "OpenClaw \u793E\u533A\u6280\u80FD\uFF0C\u7528\u4E8E X/Twitter \u8BFB\u53D6/\u641C\u7D22/\u53D1\u5E03\u5DE5\u4F5C\u6D41\u3002",
|
|
1330
|
+
description: "\u4F7F\u7528 bird CLI \u5728\u4EE3\u7406\u5DE5\u4F5C\u6D41\u4E2D\u8BFB\u53D6\u7EBF\u7A0B\u3001\u641C\u7D22\u5E16\u5B50\u5E76\u8D77\u8349\u63A8\u6587/\u56DE\u590D\u3002"
|
|
1331
|
+
},
|
|
1332
|
+
"cloudflare-deploy": {
|
|
1333
|
+
summary: "OpenAI \u7CBE\u9009\u6280\u80FD\uFF0C\u7528\u4E8E\u5728 Cloudflare \u4E0A\u90E8\u7F72\u5E94\u7528\u4E0E\u57FA\u7840\u8BBE\u65BD\u3002",
|
|
1334
|
+
description: "\u4F7F\u7528\u8BE5\u6280\u80FD\u53EF\u9009\u62E9 Cloudflare \u4EA7\u54C1\u5E76\u90E8\u7F72 Workers\u3001Pages \u53CA\u76F8\u5173\u670D\u52A1\u3002"
|
|
1335
|
+
},
|
|
1336
|
+
"channel-plugin-discord": {
|
|
1337
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E Discord \u6E20\u9053\u96C6\u6210\u3002",
|
|
1338
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B Discord \u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1339
|
+
},
|
|
1340
|
+
"channel-plugin-telegram": {
|
|
1341
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E Telegram \u6E20\u9053\u96C6\u6210\u3002",
|
|
1342
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B Telegram \u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1343
|
+
},
|
|
1344
|
+
"channel-plugin-slack": {
|
|
1345
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E Slack \u6E20\u9053\u96C6\u6210\u3002",
|
|
1346
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B Slack \u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1347
|
+
},
|
|
1348
|
+
"channel-plugin-wecom": {
|
|
1349
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E\u4F01\u4E1A\u5FAE\u4FE1\u6E20\u9053\u96C6\u6210\u3002",
|
|
1350
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B\u4F01\u4E1A\u5FAE\u4FE1\u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1351
|
+
},
|
|
1352
|
+
"channel-plugin-email": {
|
|
1353
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E Email \u6E20\u9053\u96C6\u6210\u3002",
|
|
1354
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B Email \u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1355
|
+
},
|
|
1356
|
+
"channel-plugin-whatsapp": {
|
|
1357
|
+
summary: "NextClaw \u5B98\u65B9\u63D2\u4EF6\uFF0C\u7528\u4E8E WhatsApp \u6E20\u9053\u96C6\u6210\u3002",
|
|
1358
|
+
description: "\u901A\u8FC7 NextClaw \u63D2\u4EF6\u8FD0\u884C\u65F6\u63D0\u4F9B WhatsApp \u6E20\u9053\u7684\u5165\u7AD9/\u51FA\u7AD9\u652F\u6301\u3002"
|
|
1359
|
+
},
|
|
1360
|
+
"channel-plugin-clawbay": {
|
|
1361
|
+
summary: "Clawbay \u5B98\u65B9\u6E20\u9053\u63D2\u4EF6\uFF0C\u7528\u4E8E NextClaw \u96C6\u6210\u3002",
|
|
1362
|
+
description: "\u901A\u8FC7\u63D2\u4EF6\u8FD0\u884C\u65F6\u4E3A NextClaw \u63D0\u4F9B Clawbay \u6E20\u9053\u80FD\u529B\u3002"
|
|
1363
|
+
}
|
|
1364
|
+
};
|
|
1365
|
+
function readLocalizedMap(value) {
|
|
1366
|
+
const localized = {};
|
|
1367
|
+
if (!isRecord(value)) {
|
|
1368
|
+
return localized;
|
|
1369
|
+
}
|
|
1370
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
1371
|
+
if (typeof entry !== "string" || entry.trim().length === 0) {
|
|
1372
|
+
continue;
|
|
1373
|
+
}
|
|
1374
|
+
localized[key] = entry.trim();
|
|
1375
|
+
}
|
|
1376
|
+
return localized;
|
|
1377
|
+
}
|
|
1378
|
+
function normalizeLocaleTag(value) {
|
|
1379
|
+
return value.trim().toLowerCase().replace(/_/g, "-");
|
|
1380
|
+
}
|
|
1381
|
+
function pickLocaleFamilyValue(localized, localeFamily) {
|
|
1382
|
+
const normalizedFamily = normalizeLocaleTag(localeFamily).split("-")[0];
|
|
1383
|
+
if (!normalizedFamily) {
|
|
1384
|
+
return void 0;
|
|
1385
|
+
}
|
|
1386
|
+
let familyMatch;
|
|
1387
|
+
for (const [locale, text] of Object.entries(localized)) {
|
|
1388
|
+
const normalizedLocale = normalizeLocaleTag(locale);
|
|
1389
|
+
if (!normalizedLocale) {
|
|
1390
|
+
continue;
|
|
1391
|
+
}
|
|
1392
|
+
if (normalizedLocale === normalizedFamily) {
|
|
1393
|
+
return text;
|
|
1394
|
+
}
|
|
1395
|
+
if (!familyMatch && normalizedLocale.startsWith(`${normalizedFamily}-`)) {
|
|
1396
|
+
familyMatch = text;
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return familyMatch;
|
|
1400
|
+
}
|
|
1401
|
+
function normalizeLocalizedTextMap(primaryText, localized, zhFallback) {
|
|
1402
|
+
const next = readLocalizedMap(localized);
|
|
1403
|
+
if (!next.en) {
|
|
1404
|
+
next.en = pickLocaleFamilyValue(next, "en") ?? primaryText;
|
|
1405
|
+
}
|
|
1406
|
+
if (!next.zh) {
|
|
1407
|
+
next.zh = pickLocaleFamilyValue(next, "zh") ?? (zhFallback && zhFallback.trim().length > 0 ? zhFallback.trim() : next.en);
|
|
1408
|
+
}
|
|
1409
|
+
return next;
|
|
1410
|
+
}
|
|
1411
|
+
function normalizeMarketplaceItemForUi(item) {
|
|
1412
|
+
const zhCopy = MARKETPLACE_ZH_COPY_BY_SLUG[item.slug];
|
|
1413
|
+
const next = {
|
|
1414
|
+
...item,
|
|
1415
|
+
summaryI18n: normalizeLocalizedTextMap(item.summary, item.summaryI18n, zhCopy?.summary)
|
|
1416
|
+
};
|
|
1417
|
+
if ("description" in item && typeof item.description === "string" && item.description.trim().length > 0) {
|
|
1418
|
+
next.descriptionI18n = normalizeLocalizedTextMap(
|
|
1419
|
+
item.description,
|
|
1420
|
+
item.descriptionI18n,
|
|
1421
|
+
zhCopy?.description
|
|
1422
|
+
);
|
|
1423
|
+
}
|
|
1424
|
+
return next;
|
|
1425
|
+
}
|
|
1290
1426
|
function toPositiveInt(raw, fallback) {
|
|
1291
1427
|
if (!raw) {
|
|
1292
1428
|
return fallback;
|
|
@@ -1314,6 +1450,192 @@ function isSupportedMarketplaceSkillItem(item, knownSkillNames) {
|
|
|
1314
1450
|
}
|
|
1315
1451
|
return item.install.kind === "builtin" && knownSkillNames.has(item.install.spec);
|
|
1316
1452
|
}
|
|
1453
|
+
function splitMarkdownFrontmatter(raw) {
|
|
1454
|
+
const normalized = raw.replace(/\r\n/g, "\n");
|
|
1455
|
+
const match = normalized.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
1456
|
+
if (!match) {
|
|
1457
|
+
return { bodyRaw: normalized };
|
|
1458
|
+
}
|
|
1459
|
+
return {
|
|
1460
|
+
metadataRaw: match[1]?.trim() || void 0,
|
|
1461
|
+
bodyRaw: match[2] ?? ""
|
|
1462
|
+
};
|
|
1463
|
+
}
|
|
1464
|
+
async function loadLocalSkillMarkdown(options, skillName) {
|
|
1465
|
+
const config = loadConfigOrDefault(options.configPath);
|
|
1466
|
+
const loader = createSkillsLoader(getWorkspacePathFromConfig3(config));
|
|
1467
|
+
if (!loader) {
|
|
1468
|
+
return null;
|
|
1469
|
+
}
|
|
1470
|
+
const skillInfo = loader.listSkills(false).find((skill) => skill.name === skillName);
|
|
1471
|
+
if (!skillInfo) {
|
|
1472
|
+
return null;
|
|
1473
|
+
}
|
|
1474
|
+
try {
|
|
1475
|
+
const raw = await readFile(skillInfo.path, "utf-8");
|
|
1476
|
+
return {
|
|
1477
|
+
raw,
|
|
1478
|
+
source: skillInfo.source
|
|
1479
|
+
};
|
|
1480
|
+
} catch {
|
|
1481
|
+
return null;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
function parseGitSkillSpec(rawSpec) {
|
|
1485
|
+
const spec = rawSpec.trim();
|
|
1486
|
+
if (!spec) {
|
|
1487
|
+
return null;
|
|
1488
|
+
}
|
|
1489
|
+
const segments = spec.split("/").filter(Boolean);
|
|
1490
|
+
if (segments.length < 3) {
|
|
1491
|
+
return null;
|
|
1492
|
+
}
|
|
1493
|
+
return {
|
|
1494
|
+
owner: segments[0] ?? "",
|
|
1495
|
+
repo: segments[1] ?? "",
|
|
1496
|
+
skillPath: segments.slice(2).join("/")
|
|
1497
|
+
};
|
|
1498
|
+
}
|
|
1499
|
+
async function fetchTextWithFallback(urls) {
|
|
1500
|
+
for (const url of urls) {
|
|
1501
|
+
try {
|
|
1502
|
+
const response = await fetch(url, {
|
|
1503
|
+
method: "GET",
|
|
1504
|
+
headers: {
|
|
1505
|
+
Accept: "text/plain, text/markdown, application/json"
|
|
1506
|
+
}
|
|
1507
|
+
});
|
|
1508
|
+
if (!response.ok) {
|
|
1509
|
+
continue;
|
|
1510
|
+
}
|
|
1511
|
+
const text = await response.text();
|
|
1512
|
+
if (text.trim().length === 0) {
|
|
1513
|
+
continue;
|
|
1514
|
+
}
|
|
1515
|
+
return { text, url };
|
|
1516
|
+
} catch {
|
|
1517
|
+
continue;
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
async function loadGitSkillMarkdownFromSpec(rawSpec) {
|
|
1523
|
+
const parsed = parseGitSkillSpec(rawSpec);
|
|
1524
|
+
if (!parsed) {
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
const candidates = [
|
|
1528
|
+
`https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/main/${parsed.skillPath}/SKILL.md`,
|
|
1529
|
+
`https://raw.githubusercontent.com/${parsed.owner}/${parsed.repo}/master/${parsed.skillPath}/SKILL.md`
|
|
1530
|
+
];
|
|
1531
|
+
const result = await fetchTextWithFallback(candidates);
|
|
1532
|
+
if (!result) {
|
|
1533
|
+
return null;
|
|
1534
|
+
}
|
|
1535
|
+
return {
|
|
1536
|
+
raw: result.text,
|
|
1537
|
+
sourceUrl: result.url
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
async function loadPluginReadmeFromNpm(spec) {
|
|
1541
|
+
const encodedSpec = encodeURIComponent(spec);
|
|
1542
|
+
const registryUrl = `https://registry.npmjs.org/${encodedSpec}`;
|
|
1543
|
+
try {
|
|
1544
|
+
const response = await fetch(registryUrl, {
|
|
1545
|
+
headers: {
|
|
1546
|
+
Accept: "application/json"
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
if (!response.ok) {
|
|
1550
|
+
return null;
|
|
1551
|
+
}
|
|
1552
|
+
const payload = await response.json();
|
|
1553
|
+
const readme = typeof payload.readme === "string" ? payload.readme : "";
|
|
1554
|
+
const latest = isRecord(payload["dist-tags"]) && typeof payload["dist-tags"].latest === "string" ? payload["dist-tags"].latest : void 0;
|
|
1555
|
+
const metadata = {
|
|
1556
|
+
name: typeof payload.name === "string" ? payload.name : spec,
|
|
1557
|
+
version: latest,
|
|
1558
|
+
description: typeof payload.description === "string" ? payload.description : void 0,
|
|
1559
|
+
homepage: typeof payload.homepage === "string" ? payload.homepage : void 0
|
|
1560
|
+
};
|
|
1561
|
+
if (readme.trim().length === 0) {
|
|
1562
|
+
return null;
|
|
1563
|
+
}
|
|
1564
|
+
return {
|
|
1565
|
+
readme,
|
|
1566
|
+
sourceUrl: registryUrl,
|
|
1567
|
+
metadataRaw: JSON.stringify(metadata, null, 2)
|
|
1568
|
+
};
|
|
1569
|
+
} catch {
|
|
1570
|
+
return null;
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1573
|
+
async function buildSkillContentView(options, item) {
|
|
1574
|
+
const local = await loadLocalSkillMarkdown(options, item.install.spec);
|
|
1575
|
+
if (local) {
|
|
1576
|
+
const split = splitMarkdownFrontmatter(local.raw);
|
|
1577
|
+
return {
|
|
1578
|
+
type: "skill",
|
|
1579
|
+
slug: item.slug,
|
|
1580
|
+
name: item.name,
|
|
1581
|
+
install: item.install,
|
|
1582
|
+
source: local.source,
|
|
1583
|
+
raw: local.raw,
|
|
1584
|
+
metadataRaw: split.metadataRaw,
|
|
1585
|
+
bodyRaw: split.bodyRaw
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
if (item.install.kind === "git") {
|
|
1589
|
+
const remote = await loadGitSkillMarkdownFromSpec(item.install.spec);
|
|
1590
|
+
if (remote) {
|
|
1591
|
+
const split = splitMarkdownFrontmatter(remote.raw);
|
|
1592
|
+
return {
|
|
1593
|
+
type: "skill",
|
|
1594
|
+
slug: item.slug,
|
|
1595
|
+
name: item.name,
|
|
1596
|
+
install: item.install,
|
|
1597
|
+
source: "git",
|
|
1598
|
+
raw: remote.raw,
|
|
1599
|
+
metadataRaw: split.metadataRaw,
|
|
1600
|
+
bodyRaw: split.bodyRaw,
|
|
1601
|
+
sourceUrl: remote.sourceUrl
|
|
1602
|
+
};
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
return null;
|
|
1606
|
+
}
|
|
1607
|
+
async function buildPluginContentView(item) {
|
|
1608
|
+
if (item.install.kind === "npm") {
|
|
1609
|
+
const npm = await loadPluginReadmeFromNpm(item.install.spec);
|
|
1610
|
+
if (npm) {
|
|
1611
|
+
return {
|
|
1612
|
+
type: "plugin",
|
|
1613
|
+
slug: item.slug,
|
|
1614
|
+
name: item.name,
|
|
1615
|
+
install: item.install,
|
|
1616
|
+
source: "npm",
|
|
1617
|
+
raw: npm.readme,
|
|
1618
|
+
bodyRaw: npm.readme,
|
|
1619
|
+
metadataRaw: npm.metadataRaw,
|
|
1620
|
+
sourceUrl: npm.sourceUrl
|
|
1621
|
+
};
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
return {
|
|
1625
|
+
type: "plugin",
|
|
1626
|
+
slug: item.slug,
|
|
1627
|
+
name: item.name,
|
|
1628
|
+
install: item.install,
|
|
1629
|
+
source: "remote",
|
|
1630
|
+
bodyRaw: item.description || item.summary || "",
|
|
1631
|
+
metadataRaw: JSON.stringify({
|
|
1632
|
+
name: item.name,
|
|
1633
|
+
author: item.author,
|
|
1634
|
+
sourceRepo: item.sourceRepo,
|
|
1635
|
+
homepage: item.homepage
|
|
1636
|
+
}, null, 2)
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1317
1639
|
async function fetchAllMarketplaceItems(params) {
|
|
1318
1640
|
const allItems = [];
|
|
1319
1641
|
let remotePage = 1;
|
|
@@ -1492,7 +1814,7 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1492
1814
|
if (!result.ok) {
|
|
1493
1815
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1494
1816
|
}
|
|
1495
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1817
|
+
const filteredItems = result.data.items.map((item) => normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(item))).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1496
1818
|
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1497
1819
|
const requestedPage = toPositiveInt(query.page, 1);
|
|
1498
1820
|
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
@@ -1516,12 +1838,28 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1516
1838
|
if (!result.ok) {
|
|
1517
1839
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1518
1840
|
}
|
|
1519
|
-
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1841
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1520
1842
|
if (!isSupportedMarketplacePluginItem(sanitized)) {
|
|
1521
1843
|
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1522
1844
|
}
|
|
1523
1845
|
return c.json(ok(sanitized));
|
|
1524
1846
|
});
|
|
1847
|
+
app.get("/api/marketplace/plugins/items/:slug/content", async (c) => {
|
|
1848
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1849
|
+
const result = await fetchMarketplaceData({
|
|
1850
|
+
baseUrl: marketplaceBaseUrl,
|
|
1851
|
+
path: `/api/v1/plugins/items/${slug}`
|
|
1852
|
+
});
|
|
1853
|
+
if (!result.ok) {
|
|
1854
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1855
|
+
}
|
|
1856
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1857
|
+
if (!isSupportedMarketplacePluginItem(sanitized)) {
|
|
1858
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1859
|
+
}
|
|
1860
|
+
const content = await buildPluginContentView(sanitized);
|
|
1861
|
+
return c.json(ok(content));
|
|
1862
|
+
});
|
|
1525
1863
|
app.post("/api/marketplace/plugins/install", async (c) => {
|
|
1526
1864
|
const body = await readJson(c.req.raw);
|
|
1527
1865
|
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
@@ -1585,7 +1923,7 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1585
1923
|
if (!result.ok) {
|
|
1586
1924
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1587
1925
|
}
|
|
1588
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1926
|
+
const filteredItems = result.data.items.map((item) => normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(item))).filter((item) => isSupportedMarketplacePluginItem(item));
|
|
1589
1927
|
return c.json(ok({
|
|
1590
1928
|
...result.data,
|
|
1591
1929
|
total: filteredItems.length,
|
|
@@ -1613,7 +1951,7 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1613
1951
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1614
1952
|
}
|
|
1615
1953
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1616
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
1954
|
+
const filteredItems = result.data.items.map((item) => normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(item))).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
1617
1955
|
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1618
1956
|
const requestedPage = toPositiveInt(query.page, 1);
|
|
1619
1957
|
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
@@ -1638,12 +1976,32 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1638
1976
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1639
1977
|
}
|
|
1640
1978
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1641
|
-
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1979
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1642
1980
|
if (!isSupportedMarketplaceSkillItem(sanitized, knownSkillNames)) {
|
|
1643
1981
|
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1644
1982
|
}
|
|
1645
1983
|
return c.json(ok(sanitized));
|
|
1646
1984
|
});
|
|
1985
|
+
app.get("/api/marketplace/skills/items/:slug/content", async (c) => {
|
|
1986
|
+
const slug = encodeURIComponent(c.req.param("slug"));
|
|
1987
|
+
const result = await fetchMarketplaceData({
|
|
1988
|
+
baseUrl: marketplaceBaseUrl,
|
|
1989
|
+
path: `/api/v1/skills/items/${slug}`
|
|
1990
|
+
});
|
|
1991
|
+
if (!result.ok) {
|
|
1992
|
+
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1993
|
+
}
|
|
1994
|
+
const knownSkillNames = collectKnownSkillNames(options);
|
|
1995
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1996
|
+
if (!isSupportedMarketplaceSkillItem(sanitized, knownSkillNames)) {
|
|
1997
|
+
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1998
|
+
}
|
|
1999
|
+
const content = await buildSkillContentView(options, sanitized);
|
|
2000
|
+
if (!content) {
|
|
2001
|
+
return c.json(err("NOT_FOUND", "skill markdown content not found"), 404);
|
|
2002
|
+
}
|
|
2003
|
+
return c.json(ok(content));
|
|
2004
|
+
});
|
|
1647
2005
|
app.post("/api/marketplace/skills/install", async (c) => {
|
|
1648
2006
|
const body = await readJson(c.req.raw);
|
|
1649
2007
|
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
@@ -1708,7 +2066,7 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1708
2066
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1709
2067
|
}
|
|
1710
2068
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1711
|
-
const filteredItems = result.data.items.map((item) => sanitizeMarketplaceItem(item)).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
2069
|
+
const filteredItems = result.data.items.map((item) => normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(item))).filter((item) => isSupportedMarketplaceSkillItem(item, knownSkillNames));
|
|
1712
2070
|
return c.json(ok({
|
|
1713
2071
|
...result.data,
|
|
1714
2072
|
total: filteredItems.length,
|
|
@@ -2153,7 +2511,7 @@ function startUiServer(options) {
|
|
|
2153
2511
|
join,
|
|
2154
2512
|
getContent: async (path) => {
|
|
2155
2513
|
try {
|
|
2156
|
-
return await
|
|
2514
|
+
return await readFile2(path);
|
|
2157
2515
|
} catch {
|
|
2158
2516
|
return null;
|
|
2159
2517
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nextclaw/server",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.26",
|
|
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.31",
|
|
19
19
|
"hono": "^4.6.2",
|
|
20
20
|
"ws": "^8.18.0",
|
|
21
|
-
"@nextclaw/core": "^0.6.
|
|
21
|
+
"@nextclaw/core": "^0.6.42"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
24
|
"@types/node": "^20.17.6",
|