aisoulhub 1.0.7 → 1.0.11

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/README.md CHANGED
@@ -1,2 +1,36 @@
1
- # install-tool
1
+ # AISoulHub CLI
2
2
 
3
+ A command-line interface tool to easily install, update, and manage AI Soul configurations and skills for OpenClaw agents. It seamlessly integrates with [AISoulHub](https://aisoulhub.com) to provide a streamlined experience.
4
+
5
+ ## Prerequisites
6
+
7
+ Before using this tool, ensure you have the following installed:
8
+
9
+ - [Node.js](https://nodejs.org/) (v16 or higher recommended)
10
+ - [OpenClaw](https://openclaw.ai/) CLI (`openclaw` command must be available in your PATH)
11
+
12
+ ## Usage
13
+
14
+ You can run `aisoulhub` directly via `npx` without needing to install it globally.
15
+
16
+ ### 1. Install an AI Soul
17
+
18
+ Downloads and installs an AI Soul package into a new or existing OpenClaw Agent.
19
+
20
+ ```bash
21
+ npx aisoulhub install <ai_soul_id>
22
+ ```
23
+
24
+ ### 2. Update an AI Soul
25
+
26
+ Updates an existing OpenClaw Agent with the latest AI Soul package configuration and skills.
27
+
28
+ ```bash
29
+ npx aisoulhub update
30
+ ```
31
+
32
+ <br />
33
+
34
+ ## License
35
+
36
+ ISC
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aisoulhub",
3
- "version": "1.0.7",
3
+ "version": "1.0.11",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -23,8 +23,8 @@
23
23
  "type": "module",
24
24
  "dependencies": {
25
25
  "@inquirer/prompts": "^8.3.2",
26
- "axios": "^1.13.6",
27
26
  "chalk": "^5.6.2",
27
+ "clawhub": "^0.9.0",
28
28
  "commander": "^14.0.3",
29
29
  "ora": "^9.3.0",
30
30
  "shelljs": "^0.10.0",
@@ -1,61 +1,120 @@
1
- import { checkOpenclaw, checkLogin } from '../utils/check.js';
2
- import { getAgents, addAgent } from '../utils/openclaw.js';
3
- import { downloadAndExtract } from '../utils/download.js';
1
+ import { checkOpenclaw } from '../utils/check.js';
2
+ import { getAgents, addAgent, setAgentIdentity } from '../utils/openclaw.js';
3
+ import { extractLocalZip } from '../utils/download.js';
4
4
  import { processMetaJson } from '../utils/meta.js';
5
- import { select, input } from '@inquirer/prompts';
5
+ import { select, input, confirm } from '@inquirer/prompts';
6
6
  import chalk from 'chalk';
7
+ import fs from 'fs';
7
8
  import path from 'path';
8
9
  import os from 'os';
10
+ import unzipper from 'unzipper';
9
11
 
10
- export async function installCommand(aiSoulId) {
11
- if (!aiSoulId) {
12
- console.error(chalk.red('❌ 错误: 必须提供 ai_soul_id,例如: aisoulhub install <ai_soul_id>'));
12
+ export async function installCommand(zipPath) {
13
+ if (!zipPath) {
14
+ console.error(chalk.red('❌ Error: zip path is required, e.g.: aisoulhub install <path_to_zip>'));
13
15
  process.exit(1);
14
16
  }
15
17
 
18
+ // 检查是否为本地 zip 文件
19
+ if (!zipPath.endsWith('.zip') || !fs.existsSync(path.resolve(zipPath))) {
20
+ console.error(chalk.red(`❌ Error: Invalid or missing local zip file at [${zipPath}]`));
21
+ process.exit(1);
22
+ }
23
+ const localZipPath = path.resolve(zipPath);
24
+
16
25
  // 1. 前置检查
17
26
  checkOpenclaw();
18
- await checkLogin();
19
27
 
20
28
  // 2. 获取当前的 agents 列表
21
29
  let agents = [];
22
30
  try {
23
31
  agents = getAgents();
24
32
  } catch (err) {
25
- console.error(chalk.yellow(`⚠️ 获取 agents 列表失败: ${err.message}`));
33
+ console.error(chalk.yellow(`⚠️ Failed to get agents list: ${err.message}`));
26
34
  }
27
35
 
28
36
  // 3. 提示用户选择或创建 Agent
29
37
  const choices = [
30
- { name: '✨ 创建新的 Agent', value: 'create_new' },
31
- ...agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: a.path }))
38
+ { name: '✨ Create new Agent', value: { name: 'create_new', path: null } },
39
+ ...agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: { name: a.name, path: a.path } }))
32
40
  ];
33
41
 
34
42
  const agentChoice = await select({
35
- message: '请选择要安装的 Agent',
43
+ message: 'Please select the Agent to install:',
36
44
  choices
37
45
  });
38
46
 
39
- let targetPath = agentChoice;
47
+ if (agentChoice.name === 'main') {
48
+ const isConfirmed = await confirm({
49
+ message: chalk.yellow('⚠️ Warning: Overwriting the "main" agent might cause unexpected issues. Are you sure you want to continue?'),
50
+ default: false
51
+ });
52
+
53
+ if (!isConfirmed) {
54
+ console.log(chalk.blue('ℹ️ Operation cancelled by user.'));
55
+ process.exit(0);
56
+ }
57
+ }
58
+
59
+ let targetPath = agentChoice.path;
60
+ let finalAgentName = agentChoice.name;
61
+
62
+ if (agentChoice.name === 'create_new') {
63
+ let defaultAgentName = '';
64
+ let identityName = '';
65
+
66
+ // 尝试从 zip 文件中的 aisoul-meta.json 获取默认 name 和 slug
67
+ try {
68
+ const directory = await unzipper.Open.file(localZipPath);
69
+ const metaFile = directory.files.find(d => d.path === 'aisoul-meta.json');
70
+ if (metaFile) {
71
+ const content = await metaFile.buffer();
72
+ const meta = JSON.parse(content.toString('utf-8'));
73
+ if (meta.slug) {
74
+ defaultAgentName = meta.slug;
75
+ }
76
+ if (meta.name) {
77
+ identityName = meta.name;
78
+ }
79
+ }
80
+ } catch (err) {
81
+ // 忽略读取 zip 出错的情况
82
+ }
40
83
 
41
- if (agentChoice === 'create_new') {
42
84
  const newAgentName = await input({
43
- message: '请输入新 Agent 的名称:',
44
- validate: (val) => val.trim().length > 0 ? true : 'Agent 名称不能为空'
85
+ message: 'Please enter the name of the new Agent:',
86
+ default: defaultAgentName,
87
+ validate: (val) => {
88
+ if (val.trim().length === 0) return 'Agent name cannot be empty';
89
+ if (val.trim().toLowerCase() === 'main') return '"main" is reserved. Choose another name.';
90
+ return true;
91
+ }
45
92
  });
93
+
94
+ finalAgentName = newAgentName;
46
95
 
47
96
  const defaultWorkspace = path.join(os.homedir(), '.openclaw', `workspace-${newAgentName}`);
48
97
 
49
98
  const newWorkspacePath = await input({
50
- message: '请输入新 Agent 的工作目录:',
99
+ message: 'Please enter the workspace directory for the new Agent:',
51
100
  default: defaultWorkspace,
52
- validate: (val) => val.trim().length > 0 ? true : '工作目录不能为空'
101
+ validate: (val) => val.trim().length > 0 ? true : 'Workspace directory cannot be empty'
53
102
  });
54
103
 
55
- console.log(chalk.cyan(`🔄 正在创建 Agent [${newAgentName}]...`));
104
+ console.log(chalk.cyan(`🔄 Creating Agent [${newAgentName}]...`));
56
105
  try {
57
106
  addAgent(newAgentName, newWorkspacePath);
58
- console.log(chalk.green(`✅ Agent [${newAgentName}] 创建成功。`));
107
+ console.log(chalk.green(`✅ Agent [${newAgentName}] created successfully.`));
108
+
109
+ // 设置 Agent 的显示名称
110
+ if (identityName) {
111
+ try {
112
+ setAgentIdentity(newAgentName, identityName);
113
+ console.log(chalk.green(`✅ Agent identity name set to [${identityName}].`));
114
+ } catch (identityErr) {
115
+ console.error(chalk.yellow(`⚠️ Warning: Failed to set identity name: ${identityErr.message}`));
116
+ }
117
+ }
59
118
 
60
119
  // 重新获取列表以获取新 agent 的路径
61
120
  const updatedAgents = getAgents();
@@ -72,20 +131,34 @@ export async function installCommand(aiSoulId) {
72
131
  }
73
132
 
74
133
  if (!targetPath) {
75
- console.error(chalk.red('❌ 错误: 无法确定 Agent 的工作目录路径。'));
134
+ console.error(chalk.red('❌ Error: Unable to determine the workspace directory path for the Agent.'));
76
135
  process.exit(1);
77
136
  }
78
137
 
79
- console.log(chalk.blue(`ℹ️ 目标工作目录: ${targetPath}`));
138
+ console.log(chalk.blue(`ℹ️ Target workspace directory: ${targetPath}`));
139
+
140
+ // 4. 解压本地 ZIP 文件
141
+ const extractSuccess = await extractLocalZip(localZipPath, targetPath);
80
142
 
81
- // 4. 下载并解压 ZIP 文件
82
- const downloadSuccess = await downloadAndExtract(aiSoulId, targetPath);
83
- if (!downloadSuccess) {
143
+ if (!extractSuccess) {
84
144
  process.exit(1);
85
145
  }
86
146
 
87
- // 5. 解析 meta.json 并安装技能
147
+ // 5. 解析 aisoul-meta.json 并安装技能
88
148
  await processMetaJson(targetPath);
89
149
 
90
- console.log(chalk.green(`🎉 成功将 ${aiSoulId} 安装到 Agent。`));
150
+ console.log(chalk.green(`🎉 Successfully installed local package to the Agent.`));
151
+
152
+ // 直接唤醒 Agent
153
+ console.log(chalk.cyan(`\n👉 Waking up the agent...`));
154
+ const { spawn } = await import('child_process');
155
+
156
+ const child = spawn('openclaw', ['agent', '--agent', finalAgentName, '--message', 'hi'], {
157
+ stdio: 'inherit',
158
+ shell: true
159
+ });
160
+
161
+ child.on('error', (error) => {
162
+ console.error(chalk.red(`❌ Failed to start the agent: ${error.message}`));
163
+ });
91
164
  }
package/src/index.js CHANGED
@@ -1,6 +1,5 @@
1
1
  import { Command } from 'commander';
2
2
  import { installCommand } from './commands/install.js';
3
- import { updateCommand } from './commands/update.js';
4
3
  import fs from 'fs';
5
4
  import path from 'path';
6
5
  import { fileURLToPath } from 'url';
@@ -16,23 +15,16 @@ export async function main() {
16
15
 
17
16
  program
18
17
  .name('aisoulhub')
19
- .description('AISoulHub CLI 命令行工具,用于安装和更新 Agent 技能与配置')
18
+ .description('AISoulHub CLI tool for installing Agent skills and configurations')
20
19
  .version(pkg.version);
21
20
 
22
21
  program
23
22
  .command('install')
24
- .description('安装新的 Agent 配置及技能')
25
- .argument('<ai_soul_id>', '指定要安装的 ai_soul_id (例如:12345)')
23
+ .description('Install new Agent configuration and skills from a local zip file')
24
+ .argument('<path_to_zip>', 'Specify the local path to the zip file')
26
25
  .action(async (aiSoulId) => {
27
26
  await installCommand(aiSoulId);
28
27
  });
29
28
 
30
- program
31
- .command('update')
32
- .description('更新已存在的 Agent 配置及技能')
33
- .action(async () => {
34
- await updateCommand();
35
- });
36
-
37
29
  program.parseAsync(process.argv);
38
30
  }
@@ -1,53 +1,9 @@
1
1
  import shell from 'shelljs';
2
- import fs from 'fs';
3
- import path from 'path';
4
- import os from 'os';
5
2
  import chalk from 'chalk';
6
- import { input } from '@inquirer/prompts';
7
3
 
8
4
  export function checkOpenclaw() {
9
5
  if (!shell.which('openclaw')) {
10
- console.error(chalk.red('❌ 错误: 未找到 openclaw 命令。请按照 https://openclaw.ai 的指引安装 openclaw,然后重试。'));
6
+ console.error(chalk.red('❌ Error: openclaw command not found. Please follow the instructions at https://openclaw.ai to install openclaw, then try again.'));
11
7
  process.exit(1);
12
8
  }
13
9
  }
14
-
15
- const CONFIG_PATH = path.join(os.homedir(), '.aisoulhub.json');
16
-
17
- export async function checkLogin() {
18
- let config = {};
19
- if (fs.existsSync(CONFIG_PATH)) {
20
- try {
21
- const content = fs.readFileSync(CONFIG_PATH, 'utf-8');
22
- config = JSON.parse(content);
23
- } catch (e) {
24
- // ignore parse error
25
- }
26
- }
27
-
28
- if (!config.email) {
29
- console.log(chalk.yellow('ℹ️ 未检测到登录信息,请输入您的邮箱进行登录:'));
30
- const email = await input({
31
- message: '请输入您的邮箱地址:',
32
- validate: (value) => {
33
- const pass = value.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
34
- if (pass) {
35
- return true;
36
- }
37
- return '请输入有效的邮箱地址';
38
- }
39
- });
40
-
41
- config.email = email;
42
- try {
43
- fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
44
- console.log(chalk.green(`✅ 登录成功!邮箱: ${email} 已保存。`));
45
- } catch (e) {
46
- console.log(chalk.yellow(`⚠️ 登录成功,但无法保存到 ${CONFIG_PATH}: ${e.message}`));
47
- }
48
- } else {
49
- console.log(chalk.blue(`ℹ️ 当前登录邮箱: ${config.email}`));
50
- }
51
-
52
- return config.email;
53
- }
@@ -1,37 +1,12 @@
1
- import axios from 'axios';
2
1
  import fs from 'fs';
3
- import path from 'path';
4
- import os from 'os';
5
2
  import unzipper from 'unzipper';
6
3
  import ora from 'ora';
7
4
  import chalk from 'chalk';
8
5
 
9
- export async function downloadAndExtract(aiSoulId, targetPath) {
10
- const url = `https://aisoulhub.com/packages/${aiSoulId}.zip`;
11
- const tempDir = os.tmpdir();
12
- const zipPath = path.join(tempDir, `${aiSoulId}.zip`);
13
-
14
- const spinner = ora(`正在下载包 ${aiSoulId}...`).start();
6
+ export async function extractLocalZip(zipPath, targetPath) {
7
+ const spinner = ora(`Extracting local package from ${zipPath}...`).start();
15
8
 
16
9
  try {
17
- const response = await axios({
18
- method: 'GET',
19
- url,
20
- responseType: 'stream',
21
- // Provide a mock user-agent just in case
22
- headers: { 'User-Agent': 'aisoulhub-cli/1.0.0' }
23
- });
24
-
25
- const writer = fs.createWriteStream(zipPath);
26
- response.data.pipe(writer);
27
-
28
- await new Promise((resolve, reject) => {
29
- writer.on('finish', resolve);
30
- writer.on('error', reject);
31
- });
32
-
33
- spinner.text = `下载完成,正在解压...`;
34
-
35
10
  // Ensure target path exists
36
11
  if (!fs.existsSync(targetPath)) {
37
12
  fs.mkdirSync(targetPath, { recursive: true });
@@ -42,16 +17,10 @@ export async function downloadAndExtract(aiSoulId, targetPath) {
42
17
  .pipe(unzipper.Extract({ path: targetPath }))
43
18
  .promise();
44
19
 
45
- spinner.succeed(chalk.green(`包 ${aiSoulId} 下载并解压成功!`));
46
-
47
- // Cleanup temp zip
48
- fs.unlinkSync(zipPath);
20
+ spinner.succeed(chalk.green(`Local package extracted successfully.`));
49
21
  return true;
50
22
  } catch (err) {
51
- spinner.fail(chalk.red(`下载或解压失败: ${err.message}`));
52
- if (fs.existsSync(zipPath)) {
53
- fs.unlinkSync(zipPath);
54
- }
23
+ spinner.fail(chalk.red(`Error: Failed to extract the local package: ${err.message}`));
55
24
  return false;
56
25
  }
57
26
  }
package/src/utils/meta.js CHANGED
@@ -4,9 +4,9 @@ import chalk from 'chalk';
4
4
  import { installSkill } from './openclaw.js';
5
5
 
6
6
  export async function processMetaJson(workspacePath) {
7
- const metaPath = path.join(workspacePath, 'meta.json');
7
+ const metaPath = path.join(workspacePath, 'aisoul-meta.json');
8
8
  if (!fs.existsSync(metaPath)) {
9
- console.log(chalk.yellow(`ℹ️ 未找到 meta.json,跳过技能安装。(${metaPath})`));
9
+ console.log(chalk.yellow(`ℹ️ aisoul-meta.json not found, skipping skill installation. (${metaPath})`));
10
10
  return;
11
11
  }
12
12
 
@@ -16,22 +16,10 @@ export async function processMetaJson(workspacePath) {
16
16
  const skills = meta.skills || [];
17
17
 
18
18
  if (skills.length === 0) {
19
- console.log(chalk.blue('ℹ️ meta.json 中没有定义需要安装的技能。'));
19
+ console.log(chalk.blue('ℹ️ No skills defined to install in aisoul-meta.json.'));
20
20
  return;
21
21
  }
22
-
23
- console.log(chalk.cyan(`🔄 开始安装 ${skills.length} 个技能...`));
24
- for (const skill of skills) {
25
- console.log(chalk.blue(`⏳ 正在安装技能: ${skill}...`));
26
- const success = installSkill(workspacePath, skill);
27
- if (success) {
28
- console.log(chalk.green(`✅ 技能 ${skill} 安装成功。`));
29
- } else {
30
- console.log(chalk.red(`❌ 技能 ${skill} 安装失败。`));
31
- }
32
- }
33
- console.log(chalk.green('🎉 所有技能安装流程结束。'));
34
22
  } catch (err) {
35
- console.error(chalk.red(`❌ 读取或解析 meta.json 失败: ${err.message}`));
23
+ console.error(chalk.red(`❌ Failed to read or parse aisoul-meta.json: ${err.message}`));
36
24
  }
37
25
  }
@@ -3,17 +3,31 @@ import shell from 'shelljs';
3
3
  export function getAgents() {
4
4
  const result = shell.exec('openclaw agents list --json', { silent: true });
5
5
  if (result.code !== 0) {
6
- throw new Error('获取 agents 列表失败: ' + result.stderr);
6
+ throw new Error('Failed to get agents list: ' + result.stderr);
7
7
  }
8
8
 
9
9
  // Try to parse JSON first
10
10
  try {
11
- const list = JSON.parse(result.stdout);
12
- if (Array.isArray(list)) {
13
- return list.map(item => ({
14
- name: item.id || item.name,
15
- path: item.workspace || item.path || item.agentDir
16
- }));
11
+ // 尝试提取标准 JSON 数组结构(截取第一个 '[' 到匹配的 ']' 的内容)
12
+ // 为了防止截取到后面的无关内容,我们查找从 '[' 开始,以 ']' 结束,并且紧跟换行或结尾的部分
13
+ const jsonMatch = result.stdout.match(/\[[\s\S]*?\n\]/);
14
+ if (jsonMatch) {
15
+ const list = JSON.parse(jsonMatch[0]);
16
+ if (Array.isArray(list)) {
17
+ return list.map(item => ({
18
+ name: item.id || item.name,
19
+ path: item.workspace || item.path || item.agentDir
20
+ }));
21
+ }
22
+ } else {
23
+ // 如果没有匹配到数组结构,则尝试直接解析
24
+ const list = JSON.parse(result.stdout);
25
+ if (Array.isArray(list)) {
26
+ return list.map(item => ({
27
+ name: item.id || item.name,
28
+ path: item.workspace || item.path || item.agentDir
29
+ }));
30
+ }
17
31
  }
18
32
  } catch (err) {
19
33
  // Not JSON, parse text
@@ -45,21 +59,39 @@ export function addAgent(name, workspacePath) {
45
59
  }
46
60
  const result = shell.exec(cmd, { silent: true });
47
61
  if (result.code !== 0) {
48
- throw new Error(`创建 agent [${name}] 失败: ` + result.stderr);
62
+ throw new Error(`Failed to create agent [${name}]: ` + result.stderr);
63
+ }
64
+ return result.stdout;
65
+ }
66
+
67
+ export function setAgentIdentity(agentName, identityName) {
68
+ const cmd = `openclaw agents set-identity --agent "${agentName}" --name "${identityName}"`;
69
+ const result = shell.exec(cmd, { silent: true });
70
+ if (result.code !== 0) {
71
+ throw new Error(`Failed to set agent identity for [${agentName}]: ` + result.stderr);
49
72
  }
50
73
  return result.stdout;
51
74
  }
52
75
 
53
- export function installSkill(workspacePath, skillStr) {
54
- // Try `openclaw mcp install` or similar
55
- const result = shell.exec(`openclaw mcp add ${skillStr}`, { cwd: workspacePath, silent: true });
76
+ export async function installSkill(workspacePath, skillStr, retryCount = 0) {
77
+ const maxRetries = 3;
78
+ const result = shell.exec(`npx clawhub install ${skillStr} --workdir "${workspacePath}"`, { silent: true });
79
+
56
80
  if (result.code !== 0) {
57
- // fallback
58
- const fallback = shell.exec(`openclaw skills add ${skillStr}`, { cwd: workspacePath, silent: true });
59
- if (fallback.code !== 0) {
60
- console.error(`安装技能 ${skillStr} 失败: `, fallback.stderr || result.stderr);
61
- return false;
81
+ const errorOutput = result.stderr || result.stdout;
82
+ if (errorOutput.includes('Rate limit exceeded')) {
83
+ if (retryCount < maxRetries) {
84
+ console.log(`⚠️ Rate limit exceeded. Waiting 5 seconds before retrying (${retryCount + 1}/${maxRetries})...`);
85
+ await new Promise(resolve => setTimeout(resolve, 5000));
86
+ return installSkill(workspacePath, skillStr, retryCount + 1);
87
+ } else {
88
+ console.error(`❌ Failed to install skill ${skillStr} after ${maxRetries} retries due to rate limit.`);
89
+ return false;
90
+ }
62
91
  }
92
+
93
+ console.error(`❌ Failed to install skill ${skillStr}: `, errorOutput);
94
+ return false;
63
95
  }
64
96
  return true;
65
97
  }
@@ -1,76 +0,0 @@
1
- import { checkOpenclaw, checkLogin } from '../utils/check.js';
2
- import { getAgents } from '../utils/openclaw.js';
3
- import { downloadAndExtract } from '../utils/download.js';
4
- import { processMetaJson } from '../utils/meta.js';
5
- import { select, input } from '@inquirer/prompts';
6
- import fs from 'fs';
7
- import path from 'path';
8
- import chalk from 'chalk';
9
-
10
- export async function updateCommand() {
11
- // 1. 前置检查
12
- checkOpenclaw();
13
- await checkLogin();
14
-
15
- // 2. 获取当前的 agents 列表
16
- let agents = [];
17
- try {
18
- agents = getAgents();
19
- } catch (err) {
20
- console.error(chalk.red(`❌ 获取 agents 列表失败: ${err.message}`));
21
- process.exit(1);
22
- }
23
-
24
- if (agents.length === 0) {
25
- console.error(chalk.yellow('⚠️ 当前没有可更新的 Agent。请先使用 install 命令安装。'));
26
- process.exit(1);
27
- }
28
-
29
- // 3. 提示用户选择 Agent
30
- const choices = agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: a.path }));
31
-
32
- const targetPath = await select({
33
- message: '请选择要更新的 Agent:',
34
- choices
35
- });
36
-
37
- if (!targetPath || !fs.existsSync(targetPath)) {
38
- console.error(chalk.red('❌ 错误: 无效的 Agent 工作目录。'));
39
- process.exit(1);
40
- }
41
-
42
- // 4. 确定 ai_soul_id
43
- let aiSoulId = null;
44
- const metaPath = path.join(targetPath, 'meta.json');
45
- if (fs.existsSync(metaPath)) {
46
- try {
47
- const content = fs.readFileSync(metaPath, 'utf-8');
48
- const meta = JSON.parse(content);
49
- if (meta.ai_soul_id) {
50
- aiSoulId = meta.ai_soul_id;
51
- console.log(chalk.blue(`ℹ️ 从 meta.json 中检测到 ai_soul_id: ${aiSoulId}`));
52
- }
53
- } catch (e) {
54
- console.warn(chalk.yellow('⚠️ 无法解析 meta.json。'));
55
- }
56
- }
57
-
58
- if (!aiSoulId) {
59
- console.log(chalk.yellow('ℹ️ 未能从当前 Agent 的 meta.json 中获取 ai_soul_id。'));
60
- aiSoulId = await input({
61
- message: '请输入需要更新的 ai_soul_id:',
62
- validate: (val) => val.trim().length > 0 ? true : 'ai_soul_id 不能为空'
63
- });
64
- }
65
-
66
- // 5. 下载并解压 ZIP 文件 (覆盖)
67
- const downloadSuccess = await downloadAndExtract(aiSoulId, targetPath);
68
- if (!downloadSuccess) {
69
- process.exit(1);
70
- }
71
-
72
- // 6. 解析 meta.json 并更新技能
73
- await processMetaJson(targetPath);
74
-
75
- console.log(chalk.green(`🎉 成功更新 Agent [${aiSoulId}]。`));
76
- }