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.
- package/lib/commands.js +105 -42
- 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
|
-
|
|
493
|
+
const cleaned = cleanCliRawText(raw);
|
|
494
|
+
if (!cleaned) return '';
|
|
485
495
|
try {
|
|
486
|
-
const url = new URL(
|
|
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,
|
|
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
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
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 运行时
|