@nextclaw/server 0.5.24 → 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 +370 -22
- 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",
|
|
@@ -60,22 +61,12 @@ var PREFERRED_PROVIDER_ORDER_INDEX = new Map(
|
|
|
60
61
|
PREFERRED_PROVIDER_ORDER.map((name, index) => [name, index])
|
|
61
62
|
);
|
|
62
63
|
var DOCS_BASE_URL = "https://docs.nextclaw.io";
|
|
63
|
-
var CHANNEL_DEFAULT_TUTORIAL_URL = `${DOCS_BASE_URL}/guide/channels`;
|
|
64
64
|
var CHANNEL_TUTORIAL_URLS = {
|
|
65
|
-
telegram: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
66
|
-
whatsapp: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
67
|
-
discord: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
68
65
|
feishu: {
|
|
69
66
|
default: `${DOCS_BASE_URL}/guide/tutorials/feishu`,
|
|
70
67
|
en: `${DOCS_BASE_URL}/en/guide/tutorials/feishu`,
|
|
71
68
|
zh: `${DOCS_BASE_URL}/zh/guide/tutorials/feishu`
|
|
72
|
-
}
|
|
73
|
-
mochat: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
74
|
-
dingtalk: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
75
|
-
wecom: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
76
|
-
email: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
77
|
-
slack: { default: CHANNEL_DEFAULT_TUTORIAL_URL },
|
|
78
|
-
qq: { default: CHANNEL_DEFAULT_TUTORIAL_URL }
|
|
69
|
+
}
|
|
79
70
|
};
|
|
80
71
|
function matchesExtraSensitivePath(path) {
|
|
81
72
|
if (path === "session" || path.startsWith("session.")) {
|
|
@@ -378,8 +369,8 @@ function buildConfigMeta(config) {
|
|
|
378
369
|
return left.name.localeCompare(right.name);
|
|
379
370
|
});
|
|
380
371
|
const channels = Object.keys(config.channels).map((name) => {
|
|
381
|
-
const tutorialUrls = CHANNEL_TUTORIAL_URLS[name]
|
|
382
|
-
const tutorialUrl = tutorialUrls
|
|
372
|
+
const tutorialUrls = CHANNEL_TUTORIAL_URLS[name];
|
|
373
|
+
const tutorialUrl = tutorialUrls?.default ?? tutorialUrls?.en ?? tutorialUrls?.zh;
|
|
383
374
|
return {
|
|
384
375
|
name,
|
|
385
376
|
displayName: name,
|
|
@@ -1297,6 +1288,141 @@ function sanitizeMarketplaceItem(item) {
|
|
|
1297
1288
|
delete next.metrics;
|
|
1298
1289
|
return next;
|
|
1299
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
|
+
}
|
|
1300
1426
|
function toPositiveInt(raw, fallback) {
|
|
1301
1427
|
if (!raw) {
|
|
1302
1428
|
return fallback;
|
|
@@ -1324,6 +1450,192 @@ function isSupportedMarketplaceSkillItem(item, knownSkillNames) {
|
|
|
1324
1450
|
}
|
|
1325
1451
|
return item.install.kind === "builtin" && knownSkillNames.has(item.install.spec);
|
|
1326
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
|
+
}
|
|
1327
1639
|
async function fetchAllMarketplaceItems(params) {
|
|
1328
1640
|
const allItems = [];
|
|
1329
1641
|
let remotePage = 1;
|
|
@@ -1502,7 +1814,7 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1502
1814
|
if (!result.ok) {
|
|
1503
1815
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1504
1816
|
}
|
|
1505
|
-
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));
|
|
1506
1818
|
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1507
1819
|
const requestedPage = toPositiveInt(query.page, 1);
|
|
1508
1820
|
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
@@ -1526,12 +1838,28 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1526
1838
|
if (!result.ok) {
|
|
1527
1839
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1528
1840
|
}
|
|
1529
|
-
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1841
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1530
1842
|
if (!isSupportedMarketplacePluginItem(sanitized)) {
|
|
1531
1843
|
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1532
1844
|
}
|
|
1533
1845
|
return c.json(ok(sanitized));
|
|
1534
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
|
+
});
|
|
1535
1863
|
app.post("/api/marketplace/plugins/install", async (c) => {
|
|
1536
1864
|
const body = await readJson(c.req.raw);
|
|
1537
1865
|
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
@@ -1595,7 +1923,7 @@ function registerPluginMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1595
1923
|
if (!result.ok) {
|
|
1596
1924
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1597
1925
|
}
|
|
1598
|
-
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));
|
|
1599
1927
|
return c.json(ok({
|
|
1600
1928
|
...result.data,
|
|
1601
1929
|
total: filteredItems.length,
|
|
@@ -1623,7 +1951,7 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1623
1951
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1624
1952
|
}
|
|
1625
1953
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1626
|
-
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));
|
|
1627
1955
|
const pageSize = Math.min(100, toPositiveInt(query.pageSize, 20));
|
|
1628
1956
|
const requestedPage = toPositiveInt(query.page, 1);
|
|
1629
1957
|
const totalPages = filteredItems.length === 0 ? 0 : Math.ceil(filteredItems.length / pageSize);
|
|
@@ -1648,12 +1976,32 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1648
1976
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1649
1977
|
}
|
|
1650
1978
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1651
|
-
const sanitized = sanitizeMarketplaceItem(result.data);
|
|
1979
|
+
const sanitized = normalizeMarketplaceItemForUi(sanitizeMarketplaceItem(result.data));
|
|
1652
1980
|
if (!isSupportedMarketplaceSkillItem(sanitized, knownSkillNames)) {
|
|
1653
1981
|
return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
1654
1982
|
}
|
|
1655
1983
|
return c.json(ok(sanitized));
|
|
1656
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
|
+
});
|
|
1657
2005
|
app.post("/api/marketplace/skills/install", async (c) => {
|
|
1658
2006
|
const body = await readJson(c.req.raw);
|
|
1659
2007
|
if (!body.ok || !body.data || typeof body.data !== "object") {
|
|
@@ -1718,7 +2066,7 @@ function registerSkillMarketplaceRoutes(app, options, marketplaceBaseUrl) {
|
|
|
1718
2066
|
return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
1719
2067
|
}
|
|
1720
2068
|
const knownSkillNames = collectKnownSkillNames(options);
|
|
1721
|
-
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));
|
|
1722
2070
|
return c.json(ok({
|
|
1723
2071
|
...result.data,
|
|
1724
2072
|
total: filteredItems.length,
|
|
@@ -2163,7 +2511,7 @@ function startUiServer(options) {
|
|
|
2163
2511
|
join,
|
|
2164
2512
|
getContent: async (path) => {
|
|
2165
2513
|
try {
|
|
2166
|
-
return await
|
|
2514
|
+
return await readFile2(path);
|
|
2167
2515
|
} catch {
|
|
2168
2516
|
return null;
|
|
2169
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",
|