@nextclaw/server 0.14.7 → 0.14.8-beta.0
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.js +84 -39
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.js
CHANGED
|
@@ -2986,9 +2986,7 @@ async function normalizeSessionProjectRoot(value) {
|
|
|
2986
2986
|
function isSessionProjectRootValidationError(error) {
|
|
2987
2987
|
return error instanceof SessionProjectRootValidationError;
|
|
2988
2988
|
}
|
|
2989
|
-
|
|
2990
|
-
//#region src/features/marketplace/configs/marketplace.constants.config.ts
|
|
2991
|
-
const DEFAULT_MARKETPLACE_API_BASE = "https://marketplace-api.nextclaw.io";
|
|
2989
|
+
const DEFAULT_MARKETPLACE_READ_API_BASES = ["https://api.nextclaw.net", "https://marketplace-api.nextclaw.io"];
|
|
2992
2990
|
const MARKETPLACE_ZH_COPY_BY_SLUG = {
|
|
2993
2991
|
weather: {
|
|
2994
2992
|
summary: "NextClaw 内置技能,用于天气查询工作流。",
|
|
@@ -3040,6 +3038,33 @@ const MARKETPLACE_ZH_COPY_BY_SLUG = {
|
|
|
3040
3038
|
}
|
|
3041
3039
|
};
|
|
3042
3040
|
//#endregion
|
|
3041
|
+
//#region src/features/marketplace/utils/marketplace-read-source.utils.ts
|
|
3042
|
+
const MARKETPLACE_FETCH_TIMEOUT_MS = 12e3;
|
|
3043
|
+
const DOMESTIC_MARKETPLACE_FETCH_TIMEOUT_MS = 2e3;
|
|
3044
|
+
const DOMESTIC_MARKETPLACE_RETRY_ATTEMPTS = 2;
|
|
3045
|
+
function resolveMarketplaceBaseUrls(options) {
|
|
3046
|
+
const configured = options.marketplace?.apiBaseUrl?.trim();
|
|
3047
|
+
if (configured) return [configured.replace(/\/$/, "")];
|
|
3048
|
+
return DEFAULT_MARKETPLACE_READ_API_BASES;
|
|
3049
|
+
}
|
|
3050
|
+
function normalizeMarketplaceBaseUrls(baseUrls, baseUrl) {
|
|
3051
|
+
const candidates = baseUrls && baseUrls.length > 0 ? baseUrls : [baseUrl ?? "https://marketplace-api.nextclaw.io"];
|
|
3052
|
+
return [...new Set(candidates.map((candidate) => candidate.trim().replace(/\/$/, "")).filter(Boolean))];
|
|
3053
|
+
}
|
|
3054
|
+
function getMarketplaceFetchOptions(baseUrl) {
|
|
3055
|
+
if (baseUrl.replace(/\/$/, "") !== "https://api.nextclaw.net") return {
|
|
3056
|
+
timeoutMs: MARKETPLACE_FETCH_TIMEOUT_MS,
|
|
3057
|
+
retryAttempts: 5
|
|
3058
|
+
};
|
|
3059
|
+
return {
|
|
3060
|
+
timeoutMs: DOMESTIC_MARKETPLACE_FETCH_TIMEOUT_MS,
|
|
3061
|
+
retryAttempts: DOMESTIC_MARKETPLACE_RETRY_ATTEMPTS
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
function shouldFallbackMarketplaceResult(status) {
|
|
3065
|
+
return status === 408 || status === 429 || status >= 500;
|
|
3066
|
+
}
|
|
3067
|
+
//#endregion
|
|
3043
3068
|
//#region src/features/marketplace/utils/marketplace-network-retry.utils.ts
|
|
3044
3069
|
const MARKETPLACE_NETWORK_RETRY_ATTEMPTS = 5;
|
|
3045
3070
|
const MARKETPLACE_NETWORK_RETRY_BASE_MS = 350;
|
|
@@ -3059,39 +3084,56 @@ function isRetryableMarketplaceNetworkError(error) {
|
|
|
3059
3084
|
if (error instanceof TypeError && error.message === "fetch failed") return true;
|
|
3060
3085
|
return false;
|
|
3061
3086
|
}
|
|
3062
|
-
async function runWithMarketplaceNetworkRetry(action) {
|
|
3087
|
+
async function runWithMarketplaceNetworkRetry(action, options = {}) {
|
|
3088
|
+
const attempts = options.attempts ?? MARKETPLACE_NETWORK_RETRY_ATTEMPTS;
|
|
3089
|
+
const baseDelayMs = options.baseDelayMs ?? MARKETPLACE_NETWORK_RETRY_BASE_MS;
|
|
3063
3090
|
let lastError;
|
|
3064
|
-
for (let attempt = 1; attempt <=
|
|
3091
|
+
for (let attempt = 1; attempt <= attempts; attempt += 1) try {
|
|
3065
3092
|
return await action();
|
|
3066
3093
|
} catch (error) {
|
|
3067
3094
|
lastError = error;
|
|
3068
|
-
if (attempt ===
|
|
3069
|
-
await sleepMs(
|
|
3095
|
+
if (attempt === attempts || !isRetryableMarketplaceNetworkError(error)) throw error;
|
|
3096
|
+
await sleepMs(baseDelayMs * 2 ** (attempt - 1));
|
|
3070
3097
|
}
|
|
3071
3098
|
throw lastError;
|
|
3072
3099
|
}
|
|
3073
3100
|
//#endregion
|
|
3074
3101
|
//#region src/features/marketplace/utils/marketplace-catalog.utils.ts
|
|
3075
|
-
const MARKETPLACE_FETCH_TIMEOUT_MS = 12e3;
|
|
3076
|
-
function normalizeMarketplaceBaseUrl(options) {
|
|
3077
|
-
const configured = options.marketplace?.apiBaseUrl?.trim();
|
|
3078
|
-
if (!configured) return DEFAULT_MARKETPLACE_API_BASE;
|
|
3079
|
-
return configured.replace(/\/$/, "");
|
|
3080
|
-
}
|
|
3081
3102
|
function toMarketplaceUrl(baseUrl, path, query = {}) {
|
|
3082
3103
|
const url = new URL(path, `${baseUrl.replace(/\/$/, "")}/`);
|
|
3083
3104
|
for (const [key, value] of Object.entries(query)) if (typeof value === "string" && value.length > 0) url.searchParams.set(key, value);
|
|
3084
3105
|
return url.toString();
|
|
3085
3106
|
}
|
|
3086
3107
|
async function fetchMarketplaceData(params) {
|
|
3087
|
-
const
|
|
3108
|
+
const { baseUrl, baseUrls: rawBaseUrls, path, query } = params;
|
|
3109
|
+
const baseUrls = normalizeMarketplaceBaseUrls(rawBaseUrls, baseUrl);
|
|
3110
|
+
let lastError = null;
|
|
3111
|
+
for (const sourceBaseUrl of baseUrls) {
|
|
3112
|
+
const result = await fetchMarketplaceDataFromBase({
|
|
3113
|
+
baseUrl: sourceBaseUrl,
|
|
3114
|
+
path,
|
|
3115
|
+
query
|
|
3116
|
+
});
|
|
3117
|
+
if (result.ok || !shouldFallbackMarketplaceResult(result.status)) return result;
|
|
3118
|
+
lastError = result;
|
|
3119
|
+
}
|
|
3120
|
+
return lastError ?? {
|
|
3121
|
+
ok: false,
|
|
3122
|
+
status: 503,
|
|
3123
|
+
message: "marketplace source is not configured"
|
|
3124
|
+
};
|
|
3125
|
+
}
|
|
3126
|
+
async function fetchMarketplaceDataFromBase(params) {
|
|
3127
|
+
const { baseUrl, path, query } = params;
|
|
3128
|
+
const endpoint = toMarketplaceUrl(baseUrl, path, query);
|
|
3129
|
+
const fetchOptions = getMarketplaceFetchOptions(baseUrl);
|
|
3088
3130
|
let response;
|
|
3089
3131
|
try {
|
|
3090
3132
|
response = await runWithMarketplaceNetworkRetry(() => fetch(endpoint, {
|
|
3091
3133
|
method: "GET",
|
|
3092
3134
|
headers: { Accept: "application/json" },
|
|
3093
|
-
signal: AbortSignal.timeout(
|
|
3094
|
-
}));
|
|
3135
|
+
signal: AbortSignal.timeout(fetchOptions.timeoutMs)
|
|
3136
|
+
}), { attempts: fetchOptions.retryAttempts });
|
|
3095
3137
|
} catch (error) {
|
|
3096
3138
|
return {
|
|
3097
3139
|
ok: false,
|
|
@@ -3186,15 +3228,17 @@ function toPositiveInt(raw, fallback) {
|
|
|
3186
3228
|
return parsed;
|
|
3187
3229
|
}
|
|
3188
3230
|
async function fetchAllMarketplaceItems(params) {
|
|
3231
|
+
const { baseUrl, baseUrls, path, query: rawQuery } = params;
|
|
3189
3232
|
const items = [];
|
|
3190
3233
|
let sort = "relevance";
|
|
3191
3234
|
let query;
|
|
3192
3235
|
for (let page = 1; page <= 20; page += 1) {
|
|
3193
3236
|
const result = await fetchMarketplaceData({
|
|
3194
|
-
baseUrl
|
|
3195
|
-
|
|
3237
|
+
baseUrl,
|
|
3238
|
+
baseUrls,
|
|
3239
|
+
path,
|
|
3196
3240
|
query: {
|
|
3197
|
-
...
|
|
3241
|
+
...rawQuery,
|
|
3198
3242
|
page: String(page),
|
|
3199
3243
|
pageSize: String(100)
|
|
3200
3244
|
}
|
|
@@ -3220,6 +3264,7 @@ async function fetchAllMarketplaceItems(params) {
|
|
|
3220
3264
|
async function fetchAllMcpMarketplaceItems(params) {
|
|
3221
3265
|
return fetchAllMarketplaceItems({
|
|
3222
3266
|
baseUrl: params.baseUrl,
|
|
3267
|
+
baseUrls: params.baseUrls,
|
|
3223
3268
|
path: "/api/v1/mcp/items",
|
|
3224
3269
|
query: params.query
|
|
3225
3270
|
});
|
|
@@ -3233,9 +3278,9 @@ function sanitizeMarketplaceItemView(item) {
|
|
|
3233
3278
|
//#endregion
|
|
3234
3279
|
//#region src/features/marketplace/controllers/mcp-marketplace.controller.ts
|
|
3235
3280
|
var McpMarketplaceController = class {
|
|
3236
|
-
constructor(options,
|
|
3281
|
+
constructor(options, marketplaceBaseUrls) {
|
|
3237
3282
|
this.options = options;
|
|
3238
|
-
this.
|
|
3283
|
+
this.marketplaceBaseUrls = marketplaceBaseUrls;
|
|
3239
3284
|
}
|
|
3240
3285
|
getInstalled = (c) => {
|
|
3241
3286
|
const records = new McpInstalledViewService({ getConfig: () => loadConfigOrDefault(this.options.configPath) }).listInstalled().map((record) => ({
|
|
@@ -3270,7 +3315,7 @@ var McpMarketplaceController = class {
|
|
|
3270
3315
|
listItems = async (c) => {
|
|
3271
3316
|
const query = c.req.query();
|
|
3272
3317
|
const result = await fetchAllMcpMarketplaceItems({
|
|
3273
|
-
|
|
3318
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3274
3319
|
query: {
|
|
3275
3320
|
q: query.q,
|
|
3276
3321
|
tag: query.tag,
|
|
@@ -3298,7 +3343,7 @@ var McpMarketplaceController = class {
|
|
|
3298
3343
|
getItem = async (c) => {
|
|
3299
3344
|
const slug = encodeURIComponent(c.req.param("slug"));
|
|
3300
3345
|
const result = await fetchMarketplaceData({
|
|
3301
|
-
|
|
3346
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3302
3347
|
path: `/api/v1/mcp/items/${slug}`
|
|
3303
3348
|
});
|
|
3304
3349
|
if (!result.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
@@ -3307,7 +3352,7 @@ var McpMarketplaceController = class {
|
|
|
3307
3352
|
getItemContent = async (c) => {
|
|
3308
3353
|
const slug = encodeURIComponent(c.req.param("slug"));
|
|
3309
3354
|
const result = await fetchMarketplaceData({
|
|
3310
|
-
|
|
3355
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3311
3356
|
path: `/api/v1/mcp/items/${slug}/content`
|
|
3312
3357
|
});
|
|
3313
3358
|
if (!result.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
@@ -3322,7 +3367,7 @@ var McpMarketplaceController = class {
|
|
|
3322
3367
|
const installer = this.options.marketplace?.installer;
|
|
3323
3368
|
if (!installer?.installMcp) return c.json(err("NOT_AVAILABLE", "mcp installer is not configured"), 503);
|
|
3324
3369
|
const itemResult = await fetchMarketplaceData({
|
|
3325
|
-
|
|
3370
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3326
3371
|
path: `/api/v1/mcp/items/${encodeURIComponent(slug)}`
|
|
3327
3372
|
});
|
|
3328
3373
|
if (!itemResult.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", itemResult.message), itemResult.status);
|
|
@@ -3386,7 +3431,7 @@ var McpMarketplaceController = class {
|
|
|
3386
3431
|
getRecommendations = async (c) => {
|
|
3387
3432
|
const query = c.req.query();
|
|
3388
3433
|
const result = await fetchMarketplaceData({
|
|
3389
|
-
|
|
3434
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3390
3435
|
path: "/api/v1/mcp/recommendations",
|
|
3391
3436
|
query: {
|
|
3392
3437
|
scene: query.scene,
|
|
@@ -3539,9 +3584,9 @@ async function manageMarketplaceSkill(params) {
|
|
|
3539
3584
|
};
|
|
3540
3585
|
}
|
|
3541
3586
|
var SkillMarketplaceController = class {
|
|
3542
|
-
constructor(options,
|
|
3587
|
+
constructor(options, marketplaceBaseUrls) {
|
|
3543
3588
|
this.options = options;
|
|
3544
|
-
this.
|
|
3589
|
+
this.marketplaceBaseUrls = marketplaceBaseUrls;
|
|
3545
3590
|
}
|
|
3546
3591
|
getInstalled = (c) => {
|
|
3547
3592
|
return c.json(ok(collectSkillMarketplaceInstalledView(this.options)));
|
|
@@ -3566,7 +3611,7 @@ var SkillMarketplaceController = class {
|
|
|
3566
3611
|
};
|
|
3567
3612
|
loadSupportedListPage = async (query) => {
|
|
3568
3613
|
const result = await fetchMarketplaceData({
|
|
3569
|
-
|
|
3614
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3570
3615
|
path: "/api/v1/skills/items",
|
|
3571
3616
|
query
|
|
3572
3617
|
});
|
|
@@ -3578,7 +3623,7 @@ var SkillMarketplaceController = class {
|
|
|
3578
3623
|
};
|
|
3579
3624
|
listScenes = async (c) => {
|
|
3580
3625
|
const result = await fetchMarketplaceData({
|
|
3581
|
-
|
|
3626
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3582
3627
|
path: "/api/v1/skills/scenes"
|
|
3583
3628
|
});
|
|
3584
3629
|
if (!result.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
@@ -3600,7 +3645,7 @@ var SkillMarketplaceController = class {
|
|
|
3600
3645
|
getItem = async (c) => {
|
|
3601
3646
|
const slug = encodeURIComponent(c.req.param("slug"));
|
|
3602
3647
|
const result = await fetchMarketplaceData({
|
|
3603
|
-
|
|
3648
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3604
3649
|
path: `/api/v1/skills/items/${slug}`
|
|
3605
3650
|
});
|
|
3606
3651
|
if (!result.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
@@ -3614,7 +3659,7 @@ var SkillMarketplaceController = class {
|
|
|
3614
3659
|
getItemContent = async (c) => {
|
|
3615
3660
|
const slug = encodeURIComponent(c.req.param("slug"));
|
|
3616
3661
|
const result = await fetchMarketplaceData({
|
|
3617
|
-
|
|
3662
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3618
3663
|
path: `/api/v1/skills/items/${slug}`
|
|
3619
3664
|
});
|
|
3620
3665
|
if (!result.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", result.message), result.status);
|
|
@@ -3624,7 +3669,7 @@ var SkillMarketplaceController = class {
|
|
|
3624
3669
|
if (unsupportedKind) return c.json(err("MARKETPLACE_CONTRACT_MISMATCH", `unsupported skill install kind from marketplace api: ${unsupportedKind}`), 502);
|
|
3625
3670
|
if (!isSupportedMarketplaceSkillItem(sanitized, knownSkillNames)) return c.json(err("NOT_FOUND", "marketplace item not supported by nextclaw"), 404);
|
|
3626
3671
|
const contentResult = await fetchMarketplaceData({
|
|
3627
|
-
|
|
3672
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3628
3673
|
path: `/api/v1/skills/items/${slug}/content`
|
|
3629
3674
|
});
|
|
3630
3675
|
if (!contentResult.ok) return c.json(err("MARKETPLACE_UNAVAILABLE", contentResult.message), contentResult.status);
|
|
@@ -3667,7 +3712,7 @@ var SkillMarketplaceController = class {
|
|
|
3667
3712
|
getRecommendations = async (c) => {
|
|
3668
3713
|
const query = c.req.query();
|
|
3669
3714
|
const result = await fetchMarketplaceData({
|
|
3670
|
-
|
|
3715
|
+
baseUrls: this.marketplaceBaseUrls,
|
|
3671
3716
|
path: "/api/v1/skills/recommendations",
|
|
3672
3717
|
query: {
|
|
3673
3718
|
scene: query.scene,
|
|
@@ -4849,7 +4894,7 @@ var ServerPathRoutesController = class {
|
|
|
4849
4894
|
//#region src/app/router.ts
|
|
4850
4895
|
const NCP_AGENT_BASE_PATH = "/api/ncp/agent";
|
|
4851
4896
|
const AGENT_RUNS_BASE_PATH = "/api/agent-runs";
|
|
4852
|
-
function createUiRouteControllers(options, authService,
|
|
4897
|
+
function createUiRouteControllers(options, authService, marketplaceBaseUrls) {
|
|
4853
4898
|
const { kernel, panelAppClientSdkScript, remoteAccess, runtimeControl, runtimeUpdate } = options;
|
|
4854
4899
|
return {
|
|
4855
4900
|
app: new AppRoutesController(options),
|
|
@@ -4869,8 +4914,8 @@ function createUiRouteControllers(options, authService, marketplaceBaseUrl) {
|
|
|
4869
4914
|
remote: remoteAccess ? new RemoteRoutesController(remoteAccess) : null,
|
|
4870
4915
|
runtimeControl: runtimeControl ? new RuntimeControlRoutesController(runtimeControl) : null,
|
|
4871
4916
|
runtimeUpdate: runtimeUpdate ? new RuntimeUpdateRoutesController(runtimeUpdate) : null,
|
|
4872
|
-
skillMarketplace: new SkillMarketplaceController(options,
|
|
4873
|
-
mcpMarketplace: new McpMarketplaceController(options,
|
|
4917
|
+
skillMarketplace: new SkillMarketplaceController(options, marketplaceBaseUrls),
|
|
4918
|
+
mcpMarketplace: new McpMarketplaceController(options, marketplaceBaseUrls)
|
|
4874
4919
|
};
|
|
4875
4920
|
}
|
|
4876
4921
|
function isRecord(value) {
|
|
@@ -5493,9 +5538,9 @@ var UiRouteRegistry = class {
|
|
|
5493
5538
|
};
|
|
5494
5539
|
function createUiRouter(options, authServiceOverride) {
|
|
5495
5540
|
const app = new Hono();
|
|
5496
|
-
const
|
|
5541
|
+
const marketplaceBaseUrls = resolveMarketplaceBaseUrls(options);
|
|
5497
5542
|
const authService = authServiceOverride ?? options.authService ?? new UiAuthService(options.kernel.accessManager ?? new AccessManager({ configPath: options.configPath }));
|
|
5498
|
-
const controllers = createUiRouteControllers(options, authService,
|
|
5543
|
+
const controllers = createUiRouteControllers(options, authService, marketplaceBaseUrls);
|
|
5499
5544
|
app.notFound((c) => c.json(err("NOT_FOUND", "endpoint not found"), 404));
|
|
5500
5545
|
app.use("/api/*", async (c, next) => {
|
|
5501
5546
|
const path = c.req.path;
|