aisoulhub 1.0.7 → 1.0.8

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,54 @@
1
- # install-tool
1
+ # AISoulHub CLI
2
+
3
+ A powerful 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
+ - [Node.js](https://nodejs.org/) (v16 or higher recommended)
9
+ - [OpenClaw](https://openclaw.ai/) CLI (`openclaw` command must be available in your PATH)
10
+
11
+ ## Usage
12
+
13
+ You can run `aisoulhub` directly via `npx` without needing to install it globally.
14
+
15
+ ### 1. Install an AI Soul
16
+
17
+ Downloads and installs an AI Soul package into a new or existing OpenClaw Agent.
18
+
19
+ ```bash
20
+ npx aisoulhub install <ai_soul_id>
21
+ ```
22
+
23
+ **What happens during install?**
24
+ - Checks for OpenClaw dependency.
25
+ - Prompts for your email (if not already logged in) and saves it to `~/.aisoulhub.json`.
26
+ - Asks you to select an existing Agent or create a new one.
27
+ - Downloads the corresponding zip package from AISoulHub.
28
+ - Extracts files (like `SOUL.md`, `AGENTS.md`, `IDENTITY.md`, `aisoul-meta.json`) into the Agent's workspace.
29
+ - Automatically reads `aisoul-meta.json` and installs all defined skills via OpenClaw.
30
+
31
+ ### 2. Update an AI Soul
32
+
33
+ Updates an existing OpenClaw Agent with the latest AI Soul package configuration and skills.
34
+
35
+ ```bash
36
+ npx aisoulhub update
37
+ ```
38
+
39
+ **What happens during update?**
40
+ - Displays a list of your existing Agents for you to select.
41
+ - Attempts to read the `ai_soul_id` from the Agent's `aisoul-meta.json`. If missing, prompts you to input it.
42
+ - Downloads the latest zip package from AISoulHub.
43
+ - Extracts and overwrites the existing configuration files in the workspace.
44
+ - Updates/re-installs the skills defined in the new `aisoul-meta.json`.
45
+
46
+ ## Configuration
47
+
48
+ The CLI saves a minimal configuration file at `~/.aisoulhub.json` which currently stores your login email to streamline subsequent command executions.
49
+
50
+ ## License
51
+
52
+ ISC
53
+
2
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aisoulhub",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -25,6 +25,7 @@
25
25
  "@inquirer/prompts": "^8.3.2",
26
26
  "axios": "^1.13.6",
27
27
  "chalk": "^5.6.2",
28
+ "clawhub": "^0.9.0",
28
29
  "commander": "^14.0.3",
29
30
  "ora": "^9.3.0",
30
31
  "shelljs": "^0.10.0",
@@ -2,14 +2,14 @@ import { checkOpenclaw, checkLogin } from '../utils/check.js';
2
2
  import { getAgents, addAgent } from '../utils/openclaw.js';
3
3
  import { downloadAndExtract } 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
7
  import path from 'path';
8
8
  import os from 'os';
9
9
 
10
10
  export async function installCommand(aiSoulId) {
11
11
  if (!aiSoulId) {
12
- console.error(chalk.red('❌ 错误: 必须提供 ai_soul_id,例如: aisoulhub install <ai_soul_id>'));
12
+ console.error(chalk.red('❌ Error: ai_soul_id is required, e.g.: aisoulhub install <ai_soul_id>'));
13
13
  process.exit(1);
14
14
  }
15
15
 
@@ -22,40 +22,52 @@ export async function installCommand(aiSoulId) {
22
22
  try {
23
23
  agents = getAgents();
24
24
  } catch (err) {
25
- console.error(chalk.yellow(`⚠️ 获取 agents 列表失败: ${err.message}`));
25
+ console.error(chalk.yellow(`⚠️ Failed to get agents list: ${err.message}`));
26
26
  }
27
27
 
28
28
  // 3. 提示用户选择或创建 Agent
29
29
  const choices = [
30
- { name: '✨ 创建新的 Agent', value: 'create_new' },
31
- ...agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: a.path }))
30
+ { name: '✨ Create new Agent', value: { name: 'create_new', path: null } },
31
+ ...agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: { name: a.name, path: a.path } }))
32
32
  ];
33
33
 
34
34
  const agentChoice = await select({
35
- message: '请选择要安装的 Agent',
35
+ message: 'Please select the Agent to install:',
36
36
  choices
37
37
  });
38
38
 
39
- let targetPath = agentChoice;
39
+ if (agentChoice.name === 'main') {
40
+ const isConfirmed = await confirm({
41
+ message: chalk.yellow('⚠️ Warning: Overwriting the "main" agent might cause unexpected issues. Are you sure you want to continue?'),
42
+ default: false
43
+ });
44
+
45
+ if (!isConfirmed) {
46
+ console.log(chalk.blue('ℹ️ Operation cancelled by user.'));
47
+ process.exit(0);
48
+ }
49
+ }
50
+
51
+ let targetPath = agentChoice.path;
40
52
 
41
- if (agentChoice === 'create_new') {
53
+ if (agentChoice.name === 'create_new') {
42
54
  const newAgentName = await input({
43
- message: '请输入新 Agent 的名称:',
44
- validate: (val) => val.trim().length > 0 ? true : 'Agent 名称不能为空'
55
+ message: 'Please enter the name of the new Agent:',
56
+ validate: (val) => val.trim().length > 0 ? true : 'Agent name cannot be empty'
45
57
  });
46
58
 
47
59
  const defaultWorkspace = path.join(os.homedir(), '.openclaw', `workspace-${newAgentName}`);
48
60
 
49
61
  const newWorkspacePath = await input({
50
- message: '请输入新 Agent 的工作目录:',
62
+ message: 'Please enter the workspace directory for the new Agent:',
51
63
  default: defaultWorkspace,
52
- validate: (val) => val.trim().length > 0 ? true : '工作目录不能为空'
64
+ validate: (val) => val.trim().length > 0 ? true : 'Workspace directory cannot be empty'
53
65
  });
54
66
 
55
- console.log(chalk.cyan(`🔄 正在创建 Agent [${newAgentName}]...`));
67
+ console.log(chalk.cyan(`🔄 Creating Agent [${newAgentName}]...`));
56
68
  try {
57
69
  addAgent(newAgentName, newWorkspacePath);
58
- console.log(chalk.green(`✅ Agent [${newAgentName}] 创建成功。`));
70
+ console.log(chalk.green(`✅ Agent [${newAgentName}] created successfully.`));
59
71
 
60
72
  // 重新获取列表以获取新 agent 的路径
61
73
  const updatedAgents = getAgents();
@@ -72,11 +84,11 @@ export async function installCommand(aiSoulId) {
72
84
  }
73
85
 
74
86
  if (!targetPath) {
75
- console.error(chalk.red('❌ 错误: 无法确定 Agent 的工作目录路径。'));
87
+ console.error(chalk.red('❌ Error: Unable to determine the workspace directory path for the Agent.'));
76
88
  process.exit(1);
77
89
  }
78
90
 
79
- console.log(chalk.blue(`ℹ️ 目标工作目录: ${targetPath}`));
91
+ console.log(chalk.blue(`ℹ️ Target workspace directory: ${targetPath}`));
80
92
 
81
93
  // 4. 下载并解压 ZIP 文件
82
94
  const downloadSuccess = await downloadAndExtract(aiSoulId, targetPath);
@@ -84,8 +96,8 @@ export async function installCommand(aiSoulId) {
84
96
  process.exit(1);
85
97
  }
86
98
 
87
- // 5. 解析 meta.json 并安装技能
99
+ // 5. 解析 aisoul-meta.json 并安装技能
88
100
  await processMetaJson(targetPath);
89
101
 
90
- console.log(chalk.green(`🎉 成功将 ${aiSoulId} 安装到 Agent。`));
102
+ console.log(chalk.green(`🎉 Successfully installed ${aiSoulId} to the Agent.`));
91
103
  }
@@ -2,7 +2,7 @@ import { checkOpenclaw, checkLogin } from '../utils/check.js';
2
2
  import { getAgents } from '../utils/openclaw.js';
3
3
  import { downloadAndExtract } 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 fs from 'fs';
7
7
  import path from 'path';
8
8
  import chalk from 'chalk';
@@ -17,49 +17,63 @@ export async function updateCommand() {
17
17
  try {
18
18
  agents = getAgents();
19
19
  } catch (err) {
20
- console.error(chalk.red(`❌ 获取 agents 列表失败: ${err.message}`));
20
+ console.error(chalk.red(`❌ Failed to get agents list: ${err.message}`));
21
21
  process.exit(1);
22
22
  }
23
23
 
24
24
  if (agents.length === 0) {
25
- console.error(chalk.yellow('⚠️ 当前没有可更新的 Agent。请先使用 install 命令安装。'));
25
+ console.error(chalk.yellow('⚠️ No agents available to update. Please install one first using the install command.'));
26
26
  process.exit(1);
27
27
  }
28
28
 
29
29
  // 3. 提示用户选择 Agent
30
- const choices = agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: a.path }));
30
+ const choices = agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: { name: a.name, path: a.path } }));
31
31
 
32
- const targetPath = await select({
33
- message: '请选择要更新的 Agent',
32
+ const agentChoice = await select({
33
+ message: 'Please select the Agent to update:',
34
34
  choices
35
35
  });
36
36
 
37
+ if (agentChoice.name === 'main') {
38
+ const isConfirmed = await confirm({
39
+ message: chalk.yellow('⚠️ Warning: Overwriting the "main" agent might cause unexpected issues. Are you sure you want to continue?'),
40
+ default: false
41
+ });
42
+
43
+ if (!isConfirmed) {
44
+ console.log(chalk.blue('ℹ️ Operation cancelled by user.'));
45
+ process.exit(0);
46
+ }
47
+ }
48
+
49
+ const targetPath = agentChoice.path;
50
+
37
51
  if (!targetPath || !fs.existsSync(targetPath)) {
38
- console.error(chalk.red('❌ 错误: 无效的 Agent 工作目录。'));
52
+ console.error(chalk.red('❌ Error: Invalid Agent workspace directory.'));
39
53
  process.exit(1);
40
54
  }
41
55
 
42
56
  // 4. 确定 ai_soul_id
43
57
  let aiSoulId = null;
44
- const metaPath = path.join(targetPath, 'meta.json');
58
+ const metaPath = path.join(targetPath, 'aisoul-meta.json');
45
59
  if (fs.existsSync(metaPath)) {
46
60
  try {
47
61
  const content = fs.readFileSync(metaPath, 'utf-8');
48
62
  const meta = JSON.parse(content);
49
63
  if (meta.ai_soul_id) {
50
64
  aiSoulId = meta.ai_soul_id;
51
- console.log(chalk.blue(`ℹ️ meta.json 中检测到 ai_soul_id: ${aiSoulId}`));
65
+ console.log(chalk.blue(`ℹ️ Detected ai_soul_id from aisoul-meta.json: ${aiSoulId}`));
52
66
  }
53
67
  } catch (e) {
54
- console.warn(chalk.yellow('⚠️ 无法解析 meta.json'));
68
+ console.warn(chalk.yellow('⚠️ Unable to parse aisoul-meta.json.'));
55
69
  }
56
70
  }
57
71
 
58
72
  if (!aiSoulId) {
59
- console.log(chalk.yellow('ℹ️ 未能从当前 Agent meta.json 中获取 ai_soul_id。'));
73
+ console.log(chalk.yellow('ℹ️ Unable to get ai_soul_id from the current Agent\'s aisoul-meta.json.'));
60
74
  aiSoulId = await input({
61
- message: '请输入需要更新的 ai_soul_id',
62
- validate: (val) => val.trim().length > 0 ? true : 'ai_soul_id 不能为空'
75
+ message: 'Please enter the ai_soul_id to update:',
76
+ validate: (val) => val.trim().length > 0 ? true : 'ai_soul_id cannot be empty'
63
77
  });
64
78
  }
65
79
 
@@ -69,8 +83,8 @@ export async function updateCommand() {
69
83
  process.exit(1);
70
84
  }
71
85
 
72
- // 6. 解析 meta.json 并更新技能
86
+ // 6. 解析 aisoul-meta.json 并更新技能
73
87
  await processMetaJson(targetPath);
74
88
 
75
- console.log(chalk.green(`🎉 成功更新 Agent [${aiSoulId}]。`));
89
+ console.log(chalk.green(`🎉 Successfully updated Agent [${aiSoulId}].`));
76
90
  }
package/src/index.js CHANGED
@@ -16,20 +16,20 @@ export async function main() {
16
16
 
17
17
  program
18
18
  .name('aisoulhub')
19
- .description('AISoulHub CLI 命令行工具,用于安装和更新 Agent 技能与配置')
19
+ .description('AISoulHub CLI tool for installing and updating Agent skills and configurations')
20
20
  .version(pkg.version);
21
21
 
22
22
  program
23
23
  .command('install')
24
- .description('安装新的 Agent 配置及技能')
25
- .argument('<ai_soul_id>', '指定要安装的 ai_soul_id (例如:12345)')
24
+ .description('Install new Agent configuration and skills')
25
+ .argument('<ai_soul_id>', 'Specify the ai_soul_id to install (e.g.: 12345)')
26
26
  .action(async (aiSoulId) => {
27
27
  await installCommand(aiSoulId);
28
28
  });
29
29
 
30
30
  program
31
31
  .command('update')
32
- .description('更新已存在的 Agent 配置及技能')
32
+ .description('Update existing Agent configuration and skills')
33
33
  .action(async () => {
34
34
  await updateCommand();
35
35
  });
@@ -7,7 +7,7 @@ import { input } from '@inquirer/prompts';
7
7
 
8
8
  export function checkOpenclaw() {
9
9
  if (!shell.which('openclaw')) {
10
- console.error(chalk.red('❌ 错误: 未找到 openclaw 命令。请按照 https://openclaw.ai 的指引安装 openclaw,然后重试。'));
10
+ console.error(chalk.red('❌ Error: openclaw command not found. Please follow the instructions at https://openclaw.ai to install openclaw, then try again.'));
11
11
  process.exit(1);
12
12
  }
13
13
  }
@@ -26,27 +26,27 @@ export async function checkLogin() {
26
26
  }
27
27
 
28
28
  if (!config.email) {
29
- console.log(chalk.yellow('ℹ️ 未检测到登录信息,请输入您的邮箱进行登录:'));
29
+ console.log(chalk.yellow('ℹ️ No login information detected. Please enter your email to login:'));
30
30
  const email = await input({
31
- message: '请输入您的邮箱地址:',
31
+ message: 'Please enter your email address:',
32
32
  validate: (value) => {
33
33
  const pass = value.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
34
34
  if (pass) {
35
35
  return true;
36
36
  }
37
- return '请输入有效的邮箱地址';
37
+ return 'Please enter a valid email address';
38
38
  }
39
39
  });
40
40
 
41
41
  config.email = email;
42
42
  try {
43
43
  fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
44
- console.log(chalk.green(`✅ 登录成功!邮箱: ${email} 已保存。`));
44
+ console.log(chalk.green(`✅ Login successful! Email: ${email} saved.`));
45
45
  } catch (e) {
46
- console.log(chalk.yellow(`⚠️ 登录成功,但无法保存到 ${CONFIG_PATH}: ${e.message}`));
46
+ console.log(chalk.yellow(`⚠️ Login successful, but unable to save to ${CONFIG_PATH}: ${e.message}`));
47
47
  }
48
48
  } else {
49
- console.log(chalk.blue(`ℹ️ 当前登录邮箱: ${config.email}`));
49
+ console.log(chalk.blue(`ℹ️ Currently logged in as: ${config.email}`));
50
50
  }
51
51
 
52
52
  return config.email;
@@ -11,7 +11,7 @@ export async function downloadAndExtract(aiSoulId, targetPath) {
11
11
  const tempDir = os.tmpdir();
12
12
  const zipPath = path.join(tempDir, `${aiSoulId}.zip`);
13
13
 
14
- const spinner = ora(`正在下载包 ${aiSoulId}...`).start();
14
+ const spinner = ora(`Downloading package ${aiSoulId}...`).start();
15
15
 
16
16
  try {
17
17
  const response = await axios({
@@ -30,7 +30,7 @@ export async function downloadAndExtract(aiSoulId, targetPath) {
30
30
  writer.on('error', reject);
31
31
  });
32
32
 
33
- spinner.text = `下载完成,正在解压...`;
33
+ spinner.text = `Download complete, extracting...`;
34
34
 
35
35
  // Ensure target path exists
36
36
  if (!fs.existsSync(targetPath)) {
@@ -42,13 +42,17 @@ export async function downloadAndExtract(aiSoulId, targetPath) {
42
42
  .pipe(unzipper.Extract({ path: targetPath }))
43
43
  .promise();
44
44
 
45
- spinner.succeed(chalk.green(`包 ${aiSoulId} 下载并解压成功!`));
45
+ spinner.succeed(chalk.green(`Package ${aiSoulId} downloaded and extracted successfully.`));
46
46
 
47
47
  // Cleanup temp zip
48
48
  fs.unlinkSync(zipPath);
49
49
  return true;
50
50
  } catch (err) {
51
- spinner.fail(chalk.red(`下载或解压失败: ${err.message}`));
51
+ if (err.response && err.response.status === 404) {
52
+ spinner.fail(chalk.red(`Error: Package not found. Please check if the ai_soul_id [${aiSoulId}] is correct.`));
53
+ } else {
54
+ spinner.fail(chalk.red(`Error: Failed to download or extract the package: ${err.message}`));
55
+ }
52
56
  if (fs.existsSync(zipPath)) {
53
57
  fs.unlinkSync(zipPath);
54
58
  }
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,22 @@ 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
22
 
23
- console.log(chalk.cyan(`🔄 开始安装 ${skills.length} 个技能...`));
23
+ console.log(chalk.cyan(`🔄 Starting installation of ${skills.length} skills...`));
24
24
  for (const skill of skills) {
25
- console.log(chalk.blue(`⏳ 正在安装技能: ${skill}...`));
26
- const success = installSkill(workspacePath, skill);
25
+ console.log(chalk.blue(`⏳ Installing skill: ${skill}...`));
26
+ const success = await installSkill(workspacePath, skill);
27
27
  if (success) {
28
- console.log(chalk.green(`✅ 技能 ${skill} 安装成功。`));
28
+ console.log(chalk.green(`✅ Skill ${skill} installed successfully.`));
29
29
  } else {
30
- console.log(chalk.red(`❌ 技能 ${skill} 安装失败。`));
30
+ console.log(chalk.red(`❌ Failed to install skill ${skill}.`));
31
31
  }
32
32
  }
33
- console.log(chalk.green('🎉 所有技能安装流程结束。'));
33
+ console.log(chalk.green('🎉 All skills installation processes completed.'));
34
34
  } catch (err) {
35
- console.error(chalk.red(`❌ 读取或解析 meta.json 失败: ${err.message}`));
35
+ console.error(chalk.red(`❌ Failed to read or parse aisoul-meta.json: ${err.message}`));
36
36
  }
37
37
  }
@@ -3,7 +3,7 @@ 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
@@ -45,21 +45,30 @@ export function addAgent(name, workspacePath) {
45
45
  }
46
46
  const result = shell.exec(cmd, { silent: true });
47
47
  if (result.code !== 0) {
48
- throw new Error(`创建 agent [${name}] 失败: ` + result.stderr);
48
+ throw new Error(`Failed to create agent [${name}]: ` + result.stderr);
49
49
  }
50
50
  return result.stdout;
51
51
  }
52
52
 
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 });
53
+ export async function installSkill(workspacePath, skillStr, retryCount = 0) {
54
+ const maxRetries = 3;
55
+ const result = shell.exec(`npx clawhub install ${skillStr} --workdir "${workspacePath}"`, { silent: true });
56
+
56
57
  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;
58
+ const errorOutput = result.stderr || result.stdout;
59
+ if (errorOutput.includes('Rate limit exceeded')) {
60
+ if (retryCount < maxRetries) {
61
+ console.log(`⚠️ Rate limit exceeded. Waiting 5 seconds before retrying (${retryCount + 1}/${maxRetries})...`);
62
+ await new Promise(resolve => setTimeout(resolve, 5000));
63
+ return installSkill(workspacePath, skillStr, retryCount + 1);
64
+ } else {
65
+ console.error(`❌ Failed to install skill ${skillStr} after ${maxRetries} retries due to rate limit.`);
66
+ return false;
67
+ }
62
68
  }
69
+
70
+ console.error(`❌ Failed to install skill ${skillStr}: `, errorOutput);
71
+ return false;
63
72
  }
64
73
  return true;
65
74
  }