openclaw-weiyuan-init 1.0.94 → 1.0.95
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 +69 -34
- package/lib/identity.js +21 -5
- package/package.json +1 -1
package/lib/commands.js
CHANGED
|
@@ -10,6 +10,12 @@ const { createIdentityFile } = require('./identity');
|
|
|
10
10
|
const { checkServer, initSkill } = require('./server');
|
|
11
11
|
const { printBanner } = require('./utils');
|
|
12
12
|
const execFileAsync = promisify(execFile);
|
|
13
|
+
const NPM_BIN = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
14
|
+
const NPX_BIN = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
15
|
+
|
|
16
|
+
function cliExecOptions(cwd) {
|
|
17
|
+
return process.platform === 'win32' ? { cwd, shell: true } : { cwd };
|
|
18
|
+
}
|
|
13
19
|
|
|
14
20
|
const DEFAULT_CONFIG = {
|
|
15
21
|
workspaceName: 'workspace-weiyuan',
|
|
@@ -89,23 +95,55 @@ function renderFixedMessage(template, vars = {}) {
|
|
|
89
95
|
function printInitAccountWelcome(initInfo) {
|
|
90
96
|
if (!initInfo || typeof initInfo !== 'object') return;
|
|
91
97
|
const account = String(initInfo.account || '').trim();
|
|
92
|
-
if (!account) return;
|
|
93
98
|
const defaultPassword = String(initInfo.defaultPassword || '').trim() || '123456';
|
|
94
99
|
const rows = Array.isArray(initInfo.securityQuestions) ? initInfo.securityQuestions : [];
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
console.log(chalk.white(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
100
|
+
const recovered = Boolean(initInfo.recovered);
|
|
101
|
+
const contractText = String(initInfo.contractText || '').trim();
|
|
102
|
+
if (account) {
|
|
103
|
+
console.log(chalk.cyan('\n【已成功加入微元系统】'));
|
|
104
|
+
console.log(chalk.white(`你的默认账号为:${account}`));
|
|
105
|
+
if (!recovered || String(initInfo.defaultPassword || '').trim()) {
|
|
106
|
+
console.log(chalk.white(`初始密码为:${defaultPassword}`));
|
|
107
|
+
}
|
|
108
|
+
if (rows.length) {
|
|
109
|
+
console.log(chalk.white('随机密保问题和答案:'));
|
|
110
|
+
rows.slice(0, 2).forEach((row, idx) => {
|
|
111
|
+
const q = String((row && row.question) || '').trim() || `问题${idx + 1}`;
|
|
112
|
+
const a = String((row && row.answer) || '').trim() || '-';
|
|
113
|
+
console.log(chalk.white(`${idx + 1}. ${q}:${a}`));
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
console.log(chalk.white('请牢记这些内容,之后你可以通过官网直接登录微元系统以及更改你的账号名称和密码'));
|
|
117
|
+
console.log(chalk.white('微元系统官方网站为:https://app-ay9bunzcmltt.appmiaoda.com/'));
|
|
118
|
+
console.log(chalk.white('微元系统登录页面为:https://api.magon.com.cn/api/login'));
|
|
119
|
+
} else if (recovered) {
|
|
120
|
+
console.log(chalk.cyan('\n【微元身份已恢复】'));
|
|
121
|
+
console.log(chalk.white('系统已根据服务器中的绑定记录,重新恢复当前智能体对应的身份文件。'));
|
|
122
|
+
}
|
|
123
|
+
if (contractText) {
|
|
124
|
+
console.log(chalk.cyan('\n【主智能体行为约束】'));
|
|
125
|
+
for (const line of contractText.split(/\r?\n/)) {
|
|
126
|
+
console.log(line ? chalk.white(line) : '');
|
|
127
|
+
}
|
|
105
128
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function describeInitErrorMessage(error) {
|
|
132
|
+
const msg = String(error && error.message || error || '').trim();
|
|
133
|
+
if (!msg) return 'unknown_error';
|
|
134
|
+
if (msg.includes('no_bound_account_for_repair')) {
|
|
135
|
+
return '当前智能体没有绑定任何微元账号,请先通过“邀请加入微元”的专用命令完成接入';
|
|
136
|
+
}
|
|
137
|
+
if (msg.includes('invite_registration_required')) {
|
|
138
|
+
return '普通 init 仅用于修复或恢复身份文件;首次注册请使用“邀请加入微元”复制出的专用命令';
|
|
139
|
+
}
|
|
140
|
+
if (msg.includes('agent_register_token_used') || msg.includes('agent_already_registered')) {
|
|
141
|
+
return '该智能体已完成接入,不能再次使用邀请专用命令重复注册新账号';
|
|
142
|
+
}
|
|
143
|
+
if (msg.includes('agent_register_token_expired')) {
|
|
144
|
+
return '邀请专用命令已过期,请重新从“邀请加入微元”复制新的专用命令';
|
|
145
|
+
}
|
|
146
|
+
return msg;
|
|
109
147
|
}
|
|
110
148
|
|
|
111
149
|
function printReleaseNotes(notes) {
|
|
@@ -367,16 +405,12 @@ async function runJoin(weiyuanPath, identityPath, projectId, code, fromInit = fa
|
|
|
367
405
|
let lastError = null;
|
|
368
406
|
for (let i = 0; i < maxAttempts; i++) {
|
|
369
407
|
try {
|
|
370
|
-
await execFileAsync(
|
|
371
|
-
cwd: weiyuanPath
|
|
372
|
-
});
|
|
408
|
+
await execFileAsync(NPM_BIN, ['--prefix', weiyuanPath, 'run', 'weiyuan', '--', 'join', '--identity', identityPath, '--project', projectId, '--code', code, ...extra], cliExecOptions(weiyuanPath));
|
|
373
409
|
return;
|
|
374
410
|
} catch (_) {
|
|
375
411
|
}
|
|
376
412
|
try {
|
|
377
|
-
await execFileAsync(
|
|
378
|
-
cwd: weiyuanPath
|
|
379
|
-
});
|
|
413
|
+
await execFileAsync(NPX_BIN, ['-y', '-p', 'tsx', '-p', 'tweetnacl', 'tsx', path.join(weiyuanPath, 'src', 'cliMain.ts'), 'join', '--identity', identityPath, '--project', projectId, '--code', code, ...extra], cliExecOptions(weiyuanPath));
|
|
380
414
|
return;
|
|
381
415
|
} catch (error) {
|
|
382
416
|
lastError = error;
|
|
@@ -423,23 +457,19 @@ export const CAPABILITY_ACTION_ALLOWLIST = new Set<string>(Object.values(CAPABIL
|
|
|
423
457
|
await fs.writeFile(file, capabilityCompatContent, 'utf8');
|
|
424
458
|
}
|
|
425
459
|
}
|
|
426
|
-
await execFileAsync(
|
|
460
|
+
await execFileAsync(NPM_BIN, ['--prefix', weiyuanPath, 'install'], cliExecOptions(weiyuanPath));
|
|
427
461
|
}
|
|
428
462
|
|
|
429
463
|
async function runCliInit(weiyuanPath, identityPath, serverUrl) {
|
|
430
464
|
const maxAttempts = 3;
|
|
431
465
|
for (let i = 0; i < maxAttempts; i++) {
|
|
432
466
|
try {
|
|
433
|
-
await execFileAsync(
|
|
434
|
-
cwd: weiyuanPath
|
|
435
|
-
});
|
|
467
|
+
await execFileAsync(NPM_BIN, ['--prefix', weiyuanPath, 'run', 'weiyuan', '--', 'init', '--server', serverUrl, '--out', identityPath], cliExecOptions(weiyuanPath));
|
|
436
468
|
return true;
|
|
437
469
|
} catch (_) {
|
|
438
470
|
}
|
|
439
471
|
try {
|
|
440
|
-
await execFileAsync(
|
|
441
|
-
cwd: weiyuanPath
|
|
442
|
-
});
|
|
472
|
+
await execFileAsync(NPX_BIN, ['-y', '-p', 'tsx', '-p', 'tweetnacl', 'tsx', path.join(weiyuanPath, 'src', 'cliMain.ts'), 'init', '--server', serverUrl, '--out', identityPath], cliExecOptions(weiyuanPath));
|
|
443
473
|
return true;
|
|
444
474
|
} catch (error) {
|
|
445
475
|
if (i >= maxAttempts - 1 || !isRetryableGatewayError(error)) throw error;
|
|
@@ -474,10 +504,11 @@ async function runInit(options) {
|
|
|
474
504
|
const workspaceExists = await fs.pathExists(workspacePath);
|
|
475
505
|
const weiyuanExists = await fs.pathExists(weiyuanPath);
|
|
476
506
|
const identityExists = await fs.pathExists(identityPath);
|
|
507
|
+
const coreReady = weiyuanExists && identityExists;
|
|
477
508
|
|
|
478
509
|
// 检查是否已存在
|
|
479
510
|
if (!force && workspaceExists) {
|
|
480
|
-
if (hasInviteJoin &&
|
|
511
|
+
if (hasInviteJoin && coreReady) {
|
|
481
512
|
let spinner = ora('检测到已接入微元系统,直接加入项目...').start();
|
|
482
513
|
try {
|
|
483
514
|
await runAcrossServerCandidates(serverCandidates, 'join_existing_workspace', async (candidate) => {
|
|
@@ -499,9 +530,12 @@ async function runInit(options) {
|
|
|
499
530
|
if (hasInviteJoin) {
|
|
500
531
|
console.log(chalk.yellow('\n⚠️ 检测到已有工作目录,准备继续执行补齐流程(身份/入组)...\n'));
|
|
501
532
|
} else {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
533
|
+
if (coreReady) {
|
|
534
|
+
console.log(chalk.yellow(`\n⚠️ 工作目录已存在且文件完整: ${workspacePath}`));
|
|
535
|
+
console.log(chalk.gray(' 如需更新微元系统,请使用带 --force 的更新命令\n'));
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
console.log(chalk.yellow('\n⚠️ 检测到本地微元文件不完整,准备进入普通 init 修复流程...\n'));
|
|
505
539
|
}
|
|
506
540
|
}
|
|
507
541
|
|
|
@@ -591,13 +625,14 @@ async function runInit(options) {
|
|
|
591
625
|
try {
|
|
592
626
|
serverUrl = await runAcrossServerCandidates(serverCandidates, 'identity_init', async (candidate) => {
|
|
593
627
|
const created = await createIdentityFile(identityPath, candidate, workspacePath);
|
|
594
|
-
if (!created || !created.created) throw new Error('identity_create_failed');
|
|
628
|
+
if (!created || !created.created) throw new Error(created && created.error ? created.error : 'identity_create_failed');
|
|
595
629
|
initIdentityInfo = created.initInfo || null;
|
|
596
630
|
});
|
|
597
631
|
spinner.succeed(`身份文件: ${DEFAULT_CONFIG.identityFile}`);
|
|
598
632
|
} catch (error) {
|
|
599
|
-
|
|
600
|
-
|
|
633
|
+
const friendly = describeInitErrorMessage(error);
|
|
634
|
+
spinner.fail(`身份文件创建失败: ${friendly}`);
|
|
635
|
+
throw new Error(friendly);
|
|
601
636
|
}
|
|
602
637
|
}
|
|
603
638
|
|
package/lib/identity.js
CHANGED
|
@@ -32,11 +32,24 @@ function newKeyPair() {
|
|
|
32
32
|
return { publicKeyBase64, secretKeyBase64, lobsterId };
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function buildRecoveryHint(workspacePath) {
|
|
36
|
+
let username = '';
|
|
37
|
+
try {
|
|
38
|
+
username = String((os.userInfo && os.userInfo().username) || '').trim();
|
|
39
|
+
} catch (_) {
|
|
40
|
+
username = '';
|
|
41
|
+
}
|
|
42
|
+
const host = String(os.hostname() || '').trim().toLowerCase();
|
|
43
|
+
const workspace = String(workspacePath || '').trim().replace(/\\/g, '/').toLowerCase();
|
|
44
|
+
return `wrh_${sha256Hex([host, username.toLowerCase(), workspace].join('|'))}`;
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
async function registerIdentity(serverUrl, identity) {
|
|
36
48
|
const body = {
|
|
37
49
|
lobsterId: identity.lobsterId,
|
|
38
50
|
publicKeyBase64: identity.publicKeyBase64,
|
|
39
|
-
identityHash: identity.identityHash
|
|
51
|
+
identityHash: identity.identityHash,
|
|
52
|
+
recoveryHint: identity && identity.meta ? identity.meta.recoveryHint : undefined
|
|
40
53
|
};
|
|
41
54
|
const timestampMs = String(Date.now());
|
|
42
55
|
const nonce = `nonce_${Math.random().toString(16).slice(2)}`;
|
|
@@ -70,7 +83,9 @@ async function registerIdentity(serverUrl, identity) {
|
|
|
70
83
|
break;
|
|
71
84
|
}
|
|
72
85
|
if (!res || res.status !== 200) {
|
|
73
|
-
|
|
86
|
+
const envelope = res && res.data && typeof res.data === 'object' ? res.data : {};
|
|
87
|
+
const msg = String((envelope && (envelope.msg || envelope.message)) || `identity_register_failed_status_${lastStatus || 0}`);
|
|
88
|
+
throw new Error(msg);
|
|
74
89
|
}
|
|
75
90
|
const envelope = res.data && typeof res.data === 'object' ? res.data : {};
|
|
76
91
|
return envelope.data && typeof envelope.data === 'object' ? envelope.data : envelope;
|
|
@@ -100,7 +115,8 @@ async function createIdentityFile(identityPath, serverUrl, workspacePath) {
|
|
|
100
115
|
device_name: os.hostname(),
|
|
101
116
|
created_at: new Date().toISOString(),
|
|
102
117
|
workspace: workspacePath,
|
|
103
|
-
skill_path: `${workspacePath}/weiyuan
|
|
118
|
+
skill_path: `${workspacePath}/weiyuan`,
|
|
119
|
+
recoveryHint: buildRecoveryHint(workspacePath)
|
|
104
120
|
}
|
|
105
121
|
};
|
|
106
122
|
|
|
@@ -113,7 +129,7 @@ async function createIdentityFile(identityPath, serverUrl, workspacePath) {
|
|
|
113
129
|
await fs.writeJson(identityPath, identity, { spaces: 2 });
|
|
114
130
|
return { created: true, initInfo: initInfo || null };
|
|
115
131
|
} catch (error) {
|
|
116
|
-
return { created: false, initInfo: null };
|
|
132
|
+
return { created: false, initInfo: null, error: error && error.message ? String(error.message) : 'identity_create_failed' };
|
|
117
133
|
}
|
|
118
134
|
}
|
|
119
135
|
|
|
@@ -128,4 +144,4 @@ async function readIdentityFile(identityPath) {
|
|
|
128
144
|
}
|
|
129
145
|
}
|
|
130
146
|
|
|
131
|
-
module.exports = { createIdentityFile, readIdentityFile };
|
|
147
|
+
module.exports = { createIdentityFile, readIdentityFile, buildRecoveryHint };
|