nextclaw 0.16.12 → 0.16.14

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.
Files changed (2) hide show
  1. package/dist/cli/index.js +149 -76
  2. package/package.json +8 -8
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 fetch(`${apiBase}/api/v1/admin/skills/upsert`, {
598
- method: "POST",
599
- headers: {
600
- "content-type": "application/json",
601
- ...token ? { Authorization: `Bearer ${token}` } : {}
602
- },
603
- body: JSON.stringify({
604
- slug,
605
- name,
606
- summary,
607
- summaryI18n,
608
- description,
609
- descriptionI18n,
610
- author,
611
- tags,
612
- sourceRepo: options.sourceRepo?.trim() || metadata.sourceRepo,
613
- homepage: options.homepage?.trim() || metadata.homepage,
614
- publishedAt: options.publishedAt?.trim() || metadata.publishedAt,
615
- updatedAt: options.updatedAt?.trim() || metadata.updatedAt,
616
- files
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
- const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}`, {
664
- headers: {
665
- Accept: "application/json"
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
- const payload = await readMarketplaceEnvelope(response);
669
- if (!payload.ok || !payload.data) {
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
- const response = await fetch(`${apiBase}/api/v1/skills/items/${encodeURIComponent(slug)}/files`, {
685
- headers: {
686
- Accept: "application/json"
687
- }
688
- });
689
- const payload = await readMarketplaceEnvelope(response);
690
- if (!payload.ok || !payload.data) {
691
- const message = payload.error?.message || `marketplace skill file fetch failed: ${response.status}`;
692
- throw new Error(message);
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 (typeof entry.contentBase64 === "string" && entry.contentBase64.trim().length > 0) {
708
- normalized.contentBase64 = entry.contentBase64.trim();
742
+ if (!isRecord2(payload.data) || !Array.isArray(payload.data.files)) {
743
+ throw new Error("Invalid marketplace skill file manifest response");
709
744
  }
710
- return normalized;
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
- const response = await fetch(downloadUrl, {
717
- headers: {
718
- Accept: "application/octet-stream"
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, "");
@@ -7698,6 +7749,11 @@ var {
7698
7749
  saveConfig: saveConfig9,
7699
7750
  SessionManager
7700
7751
  } = NextclawCore;
7752
+ function applyGatewayCapabilityState(gateway, next) {
7753
+ gateway.pluginRegistry = next.pluginRegistry;
7754
+ gateway.pluginChannelBindings = next.pluginChannelBindings;
7755
+ gateway.extensionRegistry = next.extensionRegistry;
7756
+ }
7701
7757
  function createGatewayShellContext(params) {
7702
7758
  const runtimeConfigPath = getConfigPath9();
7703
7759
  const config2 = resolveConfigSecrets2(loadConfig16(), { configPath: runtimeConfigPath });
@@ -8343,6 +8399,11 @@ async function hydrateServiceCapabilities(params) {
8343
8399
  currentExtensionChannels: params.state.extensionRegistry.channels,
8344
8400
  nextExtensionChannels: nextExtensionRegistry.channels
8345
8401
  });
8402
+ applyGatewayCapabilityState(params.gateway, {
8403
+ pluginRegistry: nextPluginRegistry,
8404
+ extensionRegistry: nextExtensionRegistry,
8405
+ pluginChannelBindings: nextPluginChannelBindings
8406
+ });
8346
8407
  params.state.pluginRegistry = nextPluginRegistry;
8347
8408
  params.state.extensionRegistry = nextExtensionRegistry;
8348
8409
  params.state.pluginChannelBindings = nextPluginChannelBindings;
@@ -8540,6 +8601,12 @@ function createGatewayRuntimeState(gateway) {
8540
8601
  pluginGatewayHandles: []
8541
8602
  };
8542
8603
  }
8604
+ function applyGatewayRuntimeCapabilityState(params) {
8605
+ applyGatewayCapabilityState(params.gateway, params.next);
8606
+ params.state.pluginRegistry = params.next.pluginRegistry;
8607
+ params.state.extensionRegistry = params.next.extensionRegistry;
8608
+ params.state.pluginChannelBindings = params.next.pluginChannelBindings;
8609
+ }
8543
8610
  function configureGatewayPluginRuntime(params) {
8544
8611
  params.gateway.reloader.setApplyAgentRuntimeConfig((nextConfig) => params.gateway.runtimePool.applyRuntimeConfig(nextConfig));
8545
8612
  params.gateway.reloader.setReloadPlugins(async ({ config: nextConfig, changedPaths }) => {
@@ -8553,9 +8620,15 @@ function configureGatewayPluginRuntime(params) {
8553
8620
  pluginGatewayLogger,
8554
8621
  logPluginGatewayDiagnostics
8555
8622
  });
8556
- params.state.pluginRegistry = result.pluginRegistry;
8557
- params.state.extensionRegistry = result.extensionRegistry;
8558
- params.state.pluginChannelBindings = result.pluginChannelBindings;
8623
+ applyGatewayRuntimeCapabilityState({
8624
+ gateway: params.gateway,
8625
+ state: params.state,
8626
+ next: {
8627
+ pluginRegistry: result.pluginRegistry,
8628
+ extensionRegistry: result.extensionRegistry,
8629
+ pluginChannelBindings: result.pluginChannelBindings
8630
+ }
8631
+ });
8559
8632
  params.state.pluginUiMetadata = getPluginUiMetadataFromRegistry2(result.pluginRegistry);
8560
8633
  params.state.pluginGatewayHandles = result.pluginGatewayHandles;
8561
8634
  params.gateway.runtimePool.applyExtensionRegistry(result.extensionRegistry);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextclaw",
3
- "version": "0.16.12",
3
+ "version": "0.16.14",
4
4
  "description": "Lightweight personal AI assistant with CLI, multi-provider routing, and channel integrations.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -39,16 +39,16 @@
39
39
  "chokidar": "^3.6.0",
40
40
  "commander": "^12.1.0",
41
41
  "yaml": "^2.8.1",
42
- "@nextclaw/core": "0.11.5",
43
- "@nextclaw/ncp": "0.4.0",
44
42
  "@nextclaw/ncp-agent-runtime": "0.3.0",
43
+ "@nextclaw/ncp": "0.4.0",
44
+ "@nextclaw/core": "0.11.5",
45
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",
46
+ "@nextclaw/remote": "0.1.60",
49
47
  "@nextclaw/ncp-toolkit": "0.4.5",
50
- "@nextclaw/openclaw-compat": "0.3.42",
51
- "@nextclaw/runtime": "0.2.19"
48
+ "@nextclaw/mcp": "0.1.52",
49
+ "@nextclaw/runtime": "0.2.19",
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",