openclaw-weiyuan-init 1.0.118 → 1.0.119

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/lib/commands.js +105 -42
  2. package/package.json +1 -1
package/lib/commands.js CHANGED
@@ -60,6 +60,15 @@ const DEFAULT_SKILL_TSCONFIG = {
60
60
 
61
61
  const REQUIRED_SKILL_ACTIONS = ['project.list', 'task.list_all', 'context.candidates'];
62
62
 
63
+ function cleanCliRawText(raw) {
64
+ if (raw === undefined || raw === null) return '';
65
+ let text = String(raw).replace(/\r|\n/g, ' ').replace(/`/g, '').trim();
66
+ while ((text.startsWith('"') && text.endsWith('"')) || (text.startsWith("'") && text.endsWith("'"))) {
67
+ text = text.slice(1, -1).trim();
68
+ }
69
+ return text;
70
+ }
71
+
63
72
  function bundledSkillMetadataPath(fileName) {
64
73
  return path.join(__dirname, '..', 'workspace-weiyuan', 'weiyuan', fileName);
65
74
  }
@@ -457,7 +466,7 @@ function resolveWorkspacePath(workspaceOption) {
457
466
  function decodeInviteToken(token) {
458
467
  if (!token || typeof token !== 'string') return null;
459
468
  try {
460
- const normalized = token.replace(/-/g, '+').replace(/_/g, '/');
469
+ const normalized = cleanCliRawText(token).replace(/-/g, '+').replace(/_/g, '/');
461
470
  const pad = normalized.length % 4 === 0 ? '' : '='.repeat(4 - (normalized.length % 4));
462
471
  const raw = Buffer.from(normalized + pad, 'base64').toString('utf8');
463
472
  const parsed = JSON.parse(raw);
@@ -481,9 +490,10 @@ function decodeInviteToken(token) {
481
490
  }
482
491
 
483
492
  function normalizeServerUrl(raw) {
484
- if (!raw || typeof raw !== 'string') return '';
493
+ const cleaned = cleanCliRawText(raw);
494
+ if (!cleaned) return '';
485
495
  try {
486
- const url = new URL(raw.trim());
496
+ const url = new URL(cleaned);
487
497
  const p = (url.pathname || '/').replace(/\/+$/, '');
488
498
  if (!p || p === '/') url.pathname = '/api';
489
499
  url.hash = '';
@@ -493,6 +503,30 @@ function normalizeServerUrl(raw) {
493
503
  }
494
504
  }
495
505
 
506
+ function normalizeUpgradeUrl(raw) {
507
+ const cleaned = cleanCliRawText(raw);
508
+ if (!cleaned) return '';
509
+ try {
510
+ const url = new URL(cleaned);
511
+ url.hash = '';
512
+ return url.toString().replace(/\/+$/, '');
513
+ } catch (_) {
514
+ return '';
515
+ }
516
+ }
517
+
518
+ function normalizeDownloadUrl(raw) {
519
+ const cleaned = cleanCliRawText(raw);
520
+ if (!cleaned) return '';
521
+ try {
522
+ const url = new URL(cleaned);
523
+ url.hash = '';
524
+ return url.toString();
525
+ } catch (_) {
526
+ return '';
527
+ }
528
+ }
529
+
496
530
  function isAgentRegisterServerUrl(raw) {
497
531
  if (!raw || typeof raw !== 'string') return false;
498
532
  try {
@@ -641,6 +675,7 @@ async function printAgentConversationGuide(identityPath, projectId) {
641
675
  async function ensureSkillRuntime(weiyuanPath) {
642
676
  const pkgPath = path.join(weiyuanPath, 'package.json');
643
677
  const tsconfigPath = path.join(weiyuanPath, 'tsconfig.json');
678
+ const bundledManifestPath = bundledSkillMetadataPath('manifest.json');
644
679
  const capabilityCompatContent = `export const CAPABILITY_ACTION = {
645
680
  PROJECT_DELETE: "project.delete",
646
681
  TASK_DELETE: "task.delete",
@@ -656,8 +691,15 @@ async function ensureSkillRuntime(weiyuanPath) {
656
691
 
657
692
  export const CAPABILITY_ACTION_ALLOWLIST = new Set<string>(Object.values(CAPABILITY_ACTION))
658
693
  `;
694
+ let fallbackPackageJson = { ...DEFAULT_SKILL_PACKAGE_JSON };
695
+ try {
696
+ const bundledManifest = await fs.readJson(bundledManifestPath);
697
+ const bundledVersion = String(bundledManifest && bundledManifest.version || '').trim();
698
+ if (bundledVersion) fallbackPackageJson = { ...fallbackPackageJson, version: bundledVersion };
699
+ } catch (_) {
700
+ }
659
701
  if (!await fs.pathExists(pkgPath)) {
660
- await fs.writeJson(pkgPath, DEFAULT_SKILL_PACKAGE_JSON, { spaces: 2 });
702
+ await fs.writeJson(pkgPath, fallbackPackageJson, { spaces: 2 });
661
703
  }
662
704
  if (!await fs.pathExists(tsconfigPath)) {
663
705
  await fs.writeJson(tsconfigPath, DEFAULT_SKILL_TSCONFIG, { spaces: 2 });
@@ -706,10 +748,16 @@ async function runInit(options) {
706
748
  printBanner();
707
749
  const fixedMessages = getFixedMessages();
708
750
  let initIdentityInfo = null;
751
+ options = options || {};
752
+ options.server = cleanCliRawText(options.server || '');
753
+ options.upgrade = cleanCliRawText(options.upgrade || '');
754
+ options.download = cleanCliRawText(options.download || '');
755
+ options.invite = cleanCliRawText(options.invite || '');
756
+ options.project = cleanCliRawText(options.project || '');
757
+ options.code = cleanCliRawText(options.code || '');
709
758
 
710
759
  const invite = decodeInviteToken(options.invite);
711
760
  if (invite) {
712
- options.server = options.server || invite.api;
713
761
  options.upgrade = options.upgrade || invite.upgrade;
714
762
  options.project = options.project || invite.projectId;
715
763
  options.code = options.code || invite.code;
@@ -717,8 +765,8 @@ async function runInit(options) {
717
765
 
718
766
  const workspacePath = resolveWorkspacePath(options.workspace);
719
767
  const weiyuanPath = path.join(workspacePath, 'weiyuan');
720
- const upgradeBaseUrl = options.upgrade || DEFAULT_CONFIG.upgradeBaseUrl;
721
- const requestedDownloadUrl = options.download || DEFAULT_CONFIG.downloadUrl;
768
+ const upgradeBaseUrl = normalizeUpgradeUrl(options.upgrade || DEFAULT_CONFIG.upgradeBaseUrl) || DEFAULT_CONFIG.upgradeBaseUrl;
769
+ const requestedDownloadUrl = normalizeDownloadUrl(options.download || DEFAULT_CONFIG.downloadUrl);
722
770
  const serverCandidates = resolveServerCandidates(options, invite);
723
771
  let serverUrl = serverCandidates[0] || DEFAULT_CONFIG.serverUrl;
724
772
  const force = options.force || false;
@@ -810,42 +858,57 @@ async function runInit(options) {
810
858
  spinner = ora('检测到已有 weiyuan 运行目录,跳过下载与解压...').start();
811
859
  spinner.succeed('已复用现有 weiyuan 目录');
812
860
  } else {
813
- spinner = ora('解析下载包...').start();
814
- const source = await resolvePackageSource({
815
- downloadUrl: requestedDownloadUrl,
816
- upgradeBaseUrl,
817
- spinner
818
- });
819
- spinner.succeed(`下载源: ${source.downloadUrl}`);
820
- printReleaseNotes(source.releaseNotes);
821
-
822
- spinner = ora('下载 weiyuan skill...').start();
823
- const zipPath = path.join(weiyuanPath, source.zipFilename);
824
- const downloadSuccess = await downloadFile(source.downloadUrl, zipPath, spinner, source.sha256 || '');
825
- if (!downloadSuccess) {
826
- spinner.fail('下载失败');
827
- throw new Error('下载失败');
828
- }
829
- spinner.succeed(`下载完成: ${source.zipFilename}`);
830
-
831
- // 4. 解压
832
- spinner = ora('解压文件...').start();
833
- const extractSuccess = await extractZip(zipPath, weiyuanPath);
834
- if (!extractSuccess) {
835
- spinner.fail('解压失败');
836
- throw new Error('解压失败');
837
- }
838
- spinner.succeed('解压完成');
839
-
840
- // 5. 清理
841
- spinner = ora('清理临时文件...').start();
842
- try {
843
- await fs.remove(zipPath);
844
- await persistReleaseNotes(weiyuanPath, source.releaseNotes || null);
845
- spinner.succeed('清理完成');
846
- } catch (error) {
847
- spinner.warn('清理失败,可手动删除');
861
+ const maxDownloadAttempts = 2;
862
+ let downloadCompleted = false;
863
+ let lastDownloadError = null;
864
+ for (let attempt = 1; attempt <= maxDownloadAttempts; attempt += 1) {
865
+ let source = null;
866
+ let zipPath = '';
867
+ try {
868
+ spinner = ora(attempt > 1 ? `重新解析下载包(第 ${attempt} 次)...` : '解析下载包...').start();
869
+ source = await resolvePackageSource({
870
+ downloadUrl: requestedDownloadUrl,
871
+ upgradeBaseUrl,
872
+ spinner
873
+ });
874
+ spinner.succeed(`下载源: ${source.downloadUrl}`);
875
+ printReleaseNotes(source.releaseNotes);
876
+
877
+ spinner = ora(attempt > 1 ? `重新下载 weiyuan skill(第 ${attempt} 次)...` : '下载 weiyuan skill...').start();
878
+ zipPath = path.join(weiyuanPath, source.zipFilename);
879
+ await fs.remove(zipPath).catch(() => {});
880
+ const downloadSuccess = await downloadFile(source.downloadUrl, zipPath, spinner, source.sha256 || '');
881
+ if (!downloadSuccess) throw new Error('下载失败');
882
+ spinner.succeed(`下载完成: ${source.zipFilename}`);
883
+
884
+ spinner = ora('解压文件...').start();
885
+ const extractSuccess = await extractZip(zipPath, weiyuanPath);
886
+ if (!extractSuccess) throw new Error('解压失败');
887
+ spinner.succeed('解压完成');
888
+
889
+ spinner = ora('清理临时文件...').start();
890
+ try {
891
+ await fs.remove(zipPath);
892
+ await persistReleaseNotes(weiyuanPath, source.releaseNotes || null);
893
+ spinner.succeed('清理完成');
894
+ } catch (error) {
895
+ spinner.warn('清理失败,可手动删除');
896
+ }
897
+ downloadCompleted = true;
898
+ lastDownloadError = null;
899
+ break;
900
+ } catch (error) {
901
+ lastDownloadError = error;
902
+ if (zipPath) await fs.remove(zipPath).catch(() => {});
903
+ const msg = String(error && error.message || error || '');
904
+ const canRetry = attempt < maxDownloadAttempts && msg.includes('skill_package_hash_mismatch');
905
+ if (spinner) {
906
+ spinner.warn(canRetry ? '下载包哈希校验未通过,正在重新获取最新版本后重试...' : `下载失败:${msg || 'unknown_error'}`);
907
+ }
908
+ if (!canRetry) throw error;
909
+ }
848
910
  }
911
+ if (!downloadCompleted && lastDownloadError) throw lastDownloadError;
849
912
  }
850
913
 
851
914
  // 6. 准备 CLI 运行时
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-weiyuan-init",
3
- "version": "1.0.118",
3
+ "version": "1.0.119",
4
4
  "description": "OpenClaw Weiyuan Skill 一键初始化工具",
5
5
  "main": "bin/cli.js",
6
6
  "bin": {