nextclaw 0.16.12 → 0.16.13
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/cli/index.js +124 -73
- package/package.json +7 -7
package/dist/cli/index.js
CHANGED
|
@@ -452,6 +452,49 @@ function isRecord(value) {
|
|
|
452
452
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
453
453
|
}
|
|
454
454
|
|
|
455
|
+
// src/cli/skills/marketplace-network-retry.ts
|
|
456
|
+
var MARKETPLACE_NETWORK_RETRY_ATTEMPTS = 5;
|
|
457
|
+
var MARKETPLACE_NETWORK_RETRY_BASE_MS = 350;
|
|
458
|
+
function sleepMs(ms) {
|
|
459
|
+
return new Promise((resolve15) => {
|
|
460
|
+
setTimeout(resolve15, ms);
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
function isRetryableMarketplaceNetworkError(error) {
|
|
464
|
+
if (!(error instanceof Error)) {
|
|
465
|
+
return false;
|
|
466
|
+
}
|
|
467
|
+
if (error.name === "AbortError") {
|
|
468
|
+
return false;
|
|
469
|
+
}
|
|
470
|
+
const cause = error.cause;
|
|
471
|
+
if (cause && typeof cause === "object" && cause !== null && "code" in cause) {
|
|
472
|
+
const code = cause.code;
|
|
473
|
+
if (code === "ECONNRESET" || code === "ECONNREFUSED" || code === "ETIMEDOUT" || code === "EPIPE" || code === "ENOTFOUND" || code === "EAI_AGAIN") {
|
|
474
|
+
return true;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (error instanceof TypeError && error.message === "fetch failed") {
|
|
478
|
+
return true;
|
|
479
|
+
}
|
|
480
|
+
return false;
|
|
481
|
+
}
|
|
482
|
+
async function runWithMarketplaceNetworkRetry(action) {
|
|
483
|
+
let last;
|
|
484
|
+
for (let attempt = 1; attempt <= MARKETPLACE_NETWORK_RETRY_ATTEMPTS; attempt++) {
|
|
485
|
+
try {
|
|
486
|
+
return await action();
|
|
487
|
+
} catch (error) {
|
|
488
|
+
last = error;
|
|
489
|
+
if (attempt === MARKETPLACE_NETWORK_RETRY_ATTEMPTS || !isRetryableMarketplaceNetworkError(error)) {
|
|
490
|
+
throw error;
|
|
491
|
+
}
|
|
492
|
+
await sleepMs(MARKETPLACE_NETWORK_RETRY_BASE_MS * 2 ** (attempt - 1));
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
throw last;
|
|
496
|
+
}
|
|
497
|
+
|
|
455
498
|
// src/cli/skills/marketplace.ts
|
|
456
499
|
var DEFAULT_MARKETPLACE_API_BASE = "https://marketplace-api.nextclaw.io";
|
|
457
500
|
async function installMarketplaceSkill(options) {
|
|
@@ -594,28 +637,30 @@ async function publishMarketplaceSkill(options) {
|
|
|
594
637
|
if (options.requireExisting) {
|
|
595
638
|
await fetchMarketplaceSkillItem(apiBase, slug);
|
|
596
639
|
}
|
|
597
|
-
const response = await
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
640
|
+
const response = await runWithMarketplaceNetworkRetry(
|
|
641
|
+
() => fetch(`${apiBase}/api/v1/admin/skills/upsert`, {
|
|
642
|
+
method: "POST",
|
|
643
|
+
headers: {
|
|
644
|
+
"content-type": "application/json",
|
|
645
|
+
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
646
|
+
},
|
|
647
|
+
body: JSON.stringify({
|
|
648
|
+
slug,
|
|
649
|
+
name,
|
|
650
|
+
summary,
|
|
651
|
+
summaryI18n,
|
|
652
|
+
description,
|
|
653
|
+
descriptionI18n,
|
|
654
|
+
author,
|
|
655
|
+
tags,
|
|
656
|
+
sourceRepo: options.sourceRepo?.trim() || metadata.sourceRepo,
|
|
657
|
+
homepage: options.homepage?.trim() || metadata.homepage,
|
|
658
|
+
publishedAt: options.publishedAt?.trim() || metadata.publishedAt,
|
|
659
|
+
updatedAt: options.updatedAt?.trim() || metadata.updatedAt,
|
|
660
|
+
files
|
|
661
|
+
})
|
|
617
662
|
})
|
|
618
|
-
|
|
663
|
+
);
|
|
619
664
|
const payload = await readMarketplaceEnvelope(response);
|
|
620
665
|
if (!payload.ok || !payload.data) {
|
|
621
666
|
const message = payload.error?.message || `marketplace publish failed: HTTP ${response.status}`;
|
|
@@ -660,70 +705,76 @@ function installBuiltinSkill(workdir, destinationDir, skillName) {
|
|
|
660
705
|
cpSync(dirname(builtin.path), destinationDir, { recursive: true, force: true });
|
|
661
706
|
}
|
|
662
707
|
async function fetchMarketplaceSkillItem(apiBase, slug) {
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
708
|
+
return runWithMarketplaceNetworkRetry(async () => {
|
|
709
|
+
const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}`, {
|
|
710
|
+
headers: {
|
|
711
|
+
Accept: "application/json"
|
|
712
|
+
}
|
|
713
|
+
});
|
|
714
|
+
const payload = await readMarketplaceEnvelope(response);
|
|
715
|
+
if (!payload.ok || !payload.data) {
|
|
716
|
+
const message = payload.error?.message || `marketplace skill fetch failed: ${response.status}`;
|
|
717
|
+
throw new Error(message);
|
|
666
718
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
const message = payload.error?.message || `marketplace skill fetch failed: ${response.status}`;
|
|
671
|
-
throw new Error(message);
|
|
672
|
-
}
|
|
673
|
-
const kind = payload.data.install?.kind;
|
|
674
|
-
if (kind !== "builtin" && kind !== "marketplace") {
|
|
675
|
-
throw new Error(`Unsupported skill install kind from marketplace: ${String(kind)}`);
|
|
676
|
-
}
|
|
677
|
-
return {
|
|
678
|
-
install: {
|
|
679
|
-
kind
|
|
719
|
+
const kind = payload.data.install?.kind;
|
|
720
|
+
if (kind !== "builtin" && kind !== "marketplace") {
|
|
721
|
+
throw new Error(`Unsupported skill install kind from marketplace: ${String(kind)}`);
|
|
680
722
|
}
|
|
681
|
-
|
|
723
|
+
return {
|
|
724
|
+
install: {
|
|
725
|
+
kind
|
|
726
|
+
}
|
|
727
|
+
};
|
|
728
|
+
});
|
|
682
729
|
}
|
|
683
730
|
async function fetchMarketplaceSkillFiles(apiBase, slug) {
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (!isRecord2(payload.data) || !Array.isArray(payload.data.files)) {
|
|
695
|
-
throw new Error("Invalid marketplace skill file manifest response");
|
|
696
|
-
}
|
|
697
|
-
const files = payload.data.files.map((entry, index) => {
|
|
698
|
-
if (!isRecord2(entry) || typeof entry.path !== "string" || entry.path.trim().length === 0) {
|
|
699
|
-
throw new Error(`Invalid marketplace skill file manifest at index ${index}`);
|
|
700
|
-
}
|
|
701
|
-
const normalized = {
|
|
702
|
-
path: entry.path.trim()
|
|
703
|
-
};
|
|
704
|
-
if (typeof entry.downloadPath === "string" && entry.downloadPath.trim().length > 0) {
|
|
705
|
-
normalized.downloadPath = entry.downloadPath.trim();
|
|
731
|
+
return runWithMarketplaceNetworkRetry(async () => {
|
|
732
|
+
const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}/files`, {
|
|
733
|
+
headers: {
|
|
734
|
+
Accept: "application/json"
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
const payload = await readMarketplaceEnvelope(response);
|
|
738
|
+
if (!payload.ok || !payload.data) {
|
|
739
|
+
const message = payload.error?.message || `marketplace skill file fetch failed: ${response.status}`;
|
|
740
|
+
throw new Error(message);
|
|
706
741
|
}
|
|
707
|
-
if (
|
|
708
|
-
|
|
742
|
+
if (!isRecord2(payload.data) || !Array.isArray(payload.data.files)) {
|
|
743
|
+
throw new Error("Invalid marketplace skill file manifest response");
|
|
709
744
|
}
|
|
710
|
-
|
|
745
|
+
const files = payload.data.files.map((entry, index) => {
|
|
746
|
+
if (!isRecord2(entry) || typeof entry.path !== "string" || entry.path.trim().length === 0) {
|
|
747
|
+
throw new Error(`Invalid marketplace skill file manifest at index ${index}`);
|
|
748
|
+
}
|
|
749
|
+
const normalized = {
|
|
750
|
+
path: entry.path.trim()
|
|
751
|
+
};
|
|
752
|
+
if (typeof entry.downloadPath === "string" && entry.downloadPath.trim().length > 0) {
|
|
753
|
+
normalized.downloadPath = entry.downloadPath.trim();
|
|
754
|
+
}
|
|
755
|
+
if (typeof entry.contentBase64 === "string" && entry.contentBase64.trim().length > 0) {
|
|
756
|
+
normalized.contentBase64 = entry.contentBase64.trim();
|
|
757
|
+
}
|
|
758
|
+
return normalized;
|
|
759
|
+
});
|
|
760
|
+
return { files };
|
|
711
761
|
});
|
|
712
|
-
return { files };
|
|
713
762
|
}
|
|
714
763
|
async function fetchMarketplaceSkillFileBlob(apiBase, slug, file) {
|
|
715
764
|
const downloadUrl = resolveSkillFileDownloadUrl(apiBase, slug, file);
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
765
|
+
return runWithMarketplaceNetworkRetry(async () => {
|
|
766
|
+
const response = await fetch(downloadUrl, {
|
|
767
|
+
headers: {
|
|
768
|
+
Accept: "application/octet-stream"
|
|
769
|
+
}
|
|
770
|
+
});
|
|
771
|
+
if (!response.ok) {
|
|
772
|
+
const message = await tryReadMarketplaceError(response);
|
|
773
|
+
throw new Error(message || `marketplace skill file download failed: ${response.status}`);
|
|
719
774
|
}
|
|
775
|
+
const arrayBuffer = await response.arrayBuffer();
|
|
776
|
+
return Buffer.from(arrayBuffer);
|
|
720
777
|
});
|
|
721
|
-
if (!response.ok) {
|
|
722
|
-
const message = await tryReadMarketplaceError(response);
|
|
723
|
-
throw new Error(message || `marketplace skill file download failed: ${response.status}`);
|
|
724
|
-
}
|
|
725
|
-
const arrayBuffer = await response.arrayBuffer();
|
|
726
|
-
return Buffer.from(arrayBuffer);
|
|
727
778
|
}
|
|
728
779
|
function decodeMarketplaceFileContent(path2, contentBase64) {
|
|
729
780
|
const normalized = contentBase64.replace(/\s+/g, "");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nextclaw",
|
|
3
|
-
"version": "0.16.
|
|
3
|
+
"version": "0.16.13",
|
|
4
4
|
"description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -40,15 +40,15 @@
|
|
|
40
40
|
"commander": "^12.1.0",
|
|
41
41
|
"yaml": "^2.8.1",
|
|
42
42
|
"@nextclaw/core": "0.11.5",
|
|
43
|
+
"@nextclaw/mcp": "0.1.52",
|
|
43
44
|
"@nextclaw/ncp": "0.4.0",
|
|
44
45
|
"@nextclaw/ncp-agent-runtime": "0.3.0",
|
|
45
|
-
"@nextclaw/ncp-mcp": "0.1.52",
|
|
46
|
-
"@nextclaw/mcp": "0.1.52",
|
|
47
|
-
"@nextclaw/remote": "0.1.59",
|
|
48
|
-
"@nextclaw/server": "0.11.7",
|
|
49
46
|
"@nextclaw/ncp-toolkit": "0.4.5",
|
|
50
|
-
"@nextclaw/
|
|
51
|
-
"@nextclaw/runtime": "0.2.19"
|
|
47
|
+
"@nextclaw/ncp-mcp": "0.1.52",
|
|
48
|
+
"@nextclaw/runtime": "0.2.19",
|
|
49
|
+
"@nextclaw/remote": "0.1.60",
|
|
50
|
+
"@nextclaw/server": "0.11.8",
|
|
51
|
+
"@nextclaw/openclaw-compat": "0.3.42"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"@types/node": "^20.17.6",
|