aisoulhub 1.0.8 → 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 +3 -21
- package/package.json +1 -2
- package/src/commands/install.js +73 -12
- package/src/index.js +3 -11
- package/src/utils/check.js +0 -44
- package/src/utils/download.js +4 -39
- package/src/utils/meta.js +0 -12
- package/src/utils/openclaw.js +29 -6
- package/src/commands/update.js +0 -90
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# AISoulHub CLI
|
|
2
2
|
|
|
3
|
-
A
|
|
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
4
|
|
|
5
5
|
## Prerequisites
|
|
6
6
|
|
|
7
7
|
Before using this tool, ensure you have the following installed:
|
|
8
|
+
|
|
8
9
|
- [Node.js](https://nodejs.org/) (v16 or higher recommended)
|
|
9
10
|
- [OpenClaw](https://openclaw.ai/) CLI (`openclaw` command must be available in your PATH)
|
|
10
11
|
|
|
@@ -20,14 +21,6 @@ Downloads and installs an AI Soul package into a new or existing OpenClaw Agent.
|
|
|
20
21
|
npx aisoulhub install <ai_soul_id>
|
|
21
22
|
```
|
|
22
23
|
|
|
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
24
|
### 2. Update an AI Soul
|
|
32
25
|
|
|
33
26
|
Updates an existing OpenClaw Agent with the latest AI Soul package configuration and skills.
|
|
@@ -36,19 +29,8 @@ Updates an existing OpenClaw Agent with the latest AI Soul package configuration
|
|
|
36
29
|
npx aisoulhub update
|
|
37
30
|
```
|
|
38
31
|
|
|
39
|
-
|
|
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.
|
|
32
|
+
<br />
|
|
49
33
|
|
|
50
34
|
## License
|
|
51
35
|
|
|
52
36
|
ISC
|
|
53
|
-
|
|
54
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aisoulhub",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,7 +23,6 @@
|
|
|
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",
|
|
28
27
|
"clawhub": "^0.9.0",
|
|
29
28
|
"commander": "^14.0.3",
|
package/src/commands/install.js
CHANGED
|
@@ -1,21 +1,29 @@
|
|
|
1
|
-
import { checkOpenclaw
|
|
2
|
-
import { getAgents, addAgent } from '../utils/openclaw.js';
|
|
3
|
-
import {
|
|
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
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(
|
|
11
|
-
if (!
|
|
12
|
-
console.error(chalk.red('❌ Error:
|
|
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 = [];
|
|
@@ -49,12 +57,41 @@ export async function installCommand(aiSoulId) {
|
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
let targetPath = agentChoice.path;
|
|
60
|
+
let finalAgentName = agentChoice.name;
|
|
52
61
|
|
|
53
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
|
+
}
|
|
83
|
+
|
|
54
84
|
const newAgentName = await input({
|
|
55
85
|
message: 'Please enter the name of the new Agent:',
|
|
56
|
-
|
|
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
|
+
}
|
|
57
92
|
});
|
|
93
|
+
|
|
94
|
+
finalAgentName = newAgentName;
|
|
58
95
|
|
|
59
96
|
const defaultWorkspace = path.join(os.homedir(), '.openclaw', `workspace-${newAgentName}`);
|
|
60
97
|
|
|
@@ -69,6 +106,16 @@ export async function installCommand(aiSoulId) {
|
|
|
69
106
|
addAgent(newAgentName, newWorkspacePath);
|
|
70
107
|
console.log(chalk.green(`✅ Agent [${newAgentName}] created successfully.`));
|
|
71
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
|
+
}
|
|
118
|
+
|
|
72
119
|
// 重新获取列表以获取新 agent 的路径
|
|
73
120
|
const updatedAgents = getAgents();
|
|
74
121
|
const newAgent = updatedAgents.find(a => a.name === newAgentName);
|
|
@@ -90,14 +137,28 @@ export async function installCommand(aiSoulId) {
|
|
|
90
137
|
|
|
91
138
|
console.log(chalk.blue(`ℹ️ Target workspace directory: ${targetPath}`));
|
|
92
139
|
|
|
93
|
-
// 4.
|
|
94
|
-
const
|
|
95
|
-
|
|
140
|
+
// 4. 解压本地 ZIP 文件
|
|
141
|
+
const extractSuccess = await extractLocalZip(localZipPath, targetPath);
|
|
142
|
+
|
|
143
|
+
if (!extractSuccess) {
|
|
96
144
|
process.exit(1);
|
|
97
145
|
}
|
|
98
146
|
|
|
99
147
|
// 5. 解析 aisoul-meta.json 并安装技能
|
|
100
148
|
await processMetaJson(targetPath);
|
|
101
149
|
|
|
102
|
-
console.log(chalk.green(`🎉 Successfully installed
|
|
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
|
+
});
|
|
103
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 tool for installing
|
|
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('Install new Agent configuration and skills')
|
|
25
|
-
.argument('<
|
|
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('Update existing Agent configuration and skills')
|
|
33
|
-
.action(async () => {
|
|
34
|
-
await updateCommand();
|
|
35
|
-
});
|
|
36
|
-
|
|
37
29
|
program.parseAsync(process.argv);
|
|
38
30
|
}
|
package/src/utils/check.js
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
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')) {
|
|
@@ -11,43 +7,3 @@ export function checkOpenclaw() {
|
|
|
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('ℹ️ No login information detected. Please enter your email to login:'));
|
|
30
|
-
const email = await input({
|
|
31
|
-
message: 'Please enter your email address:',
|
|
32
|
-
validate: (value) => {
|
|
33
|
-
const pass = value.match(/^[^\s@]+@[^\s@]+\.[^\s@]+$/);
|
|
34
|
-
if (pass) {
|
|
35
|
-
return true;
|
|
36
|
-
}
|
|
37
|
-
return 'Please enter a valid email address';
|
|
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(`✅ Login successful! Email: ${email} saved.`));
|
|
45
|
-
} catch (e) {
|
|
46
|
-
console.log(chalk.yellow(`⚠️ Login successful, but unable to save to ${CONFIG_PATH}: ${e.message}`));
|
|
47
|
-
}
|
|
48
|
-
} else {
|
|
49
|
-
console.log(chalk.blue(`ℹ️ Currently logged in as: ${config.email}`));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return config.email;
|
|
53
|
-
}
|
package/src/utils/download.js
CHANGED
|
@@ -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
|
|
10
|
-
const
|
|
11
|
-
const tempDir = os.tmpdir();
|
|
12
|
-
const zipPath = path.join(tempDir, `${aiSoulId}.zip`);
|
|
13
|
-
|
|
14
|
-
const spinner = ora(`Downloading package ${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 = `Download complete, extracting...`;
|
|
34
|
-
|
|
35
10
|
// Ensure target path exists
|
|
36
11
|
if (!fs.existsSync(targetPath)) {
|
|
37
12
|
fs.mkdirSync(targetPath, { recursive: true });
|
|
@@ -42,20 +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(`
|
|
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
|
-
|
|
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
|
-
}
|
|
56
|
-
if (fs.existsSync(zipPath)) {
|
|
57
|
-
fs.unlinkSync(zipPath);
|
|
58
|
-
}
|
|
23
|
+
spinner.fail(chalk.red(`Error: Failed to extract the local package: ${err.message}`));
|
|
59
24
|
return false;
|
|
60
25
|
}
|
|
61
26
|
}
|
package/src/utils/meta.js
CHANGED
|
@@ -19,18 +19,6 @@ export async function processMetaJson(workspacePath) {
|
|
|
19
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(`🔄 Starting installation of ${skills.length} skills...`));
|
|
24
|
-
for (const skill of skills) {
|
|
25
|
-
console.log(chalk.blue(`⏳ Installing skill: ${skill}...`));
|
|
26
|
-
const success = await installSkill(workspacePath, skill);
|
|
27
|
-
if (success) {
|
|
28
|
-
console.log(chalk.green(`✅ Skill ${skill} installed successfully.`));
|
|
29
|
-
} else {
|
|
30
|
-
console.log(chalk.red(`❌ Failed to install skill ${skill}.`));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
console.log(chalk.green('🎉 All skills installation processes completed.'));
|
|
34
22
|
} catch (err) {
|
|
35
23
|
console.error(chalk.red(`❌ Failed to read or parse aisoul-meta.json: ${err.message}`));
|
|
36
24
|
}
|
package/src/utils/openclaw.js
CHANGED
|
@@ -8,12 +8,26 @@ export function getAgents() {
|
|
|
8
8
|
|
|
9
9
|
// Try to parse JSON first
|
|
10
10
|
try {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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
|
|
@@ -50,6 +64,15 @@ export function addAgent(name, workspacePath) {
|
|
|
50
64
|
return result.stdout;
|
|
51
65
|
}
|
|
52
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);
|
|
72
|
+
}
|
|
73
|
+
return result.stdout;
|
|
74
|
+
}
|
|
75
|
+
|
|
53
76
|
export async function installSkill(workspacePath, skillStr, retryCount = 0) {
|
|
54
77
|
const maxRetries = 3;
|
|
55
78
|
const result = shell.exec(`npx clawhub install ${skillStr} --workdir "${workspacePath}"`, { silent: true });
|
package/src/commands/update.js
DELETED
|
@@ -1,90 +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, confirm } 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(`❌ Failed to get agents list: ${err.message}`));
|
|
21
|
-
process.exit(1);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
if (agents.length === 0) {
|
|
25
|
-
console.error(chalk.yellow('⚠️ No agents available to update. Please install one first using the install command.'));
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// 3. 提示用户选择 Agent
|
|
30
|
-
const choices = agents.map(a => ({ name: `📁 ${a.name} (${a.path})`, value: { name: a.name, path: a.path } }));
|
|
31
|
-
|
|
32
|
-
const agentChoice = await select({
|
|
33
|
-
message: 'Please select the Agent to update:',
|
|
34
|
-
choices
|
|
35
|
-
});
|
|
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
|
-
|
|
51
|
-
if (!targetPath || !fs.existsSync(targetPath)) {
|
|
52
|
-
console.error(chalk.red('❌ Error: Invalid Agent workspace directory.'));
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// 4. 确定 ai_soul_id
|
|
57
|
-
let aiSoulId = null;
|
|
58
|
-
const metaPath = path.join(targetPath, 'aisoul-meta.json');
|
|
59
|
-
if (fs.existsSync(metaPath)) {
|
|
60
|
-
try {
|
|
61
|
-
const content = fs.readFileSync(metaPath, 'utf-8');
|
|
62
|
-
const meta = JSON.parse(content);
|
|
63
|
-
if (meta.ai_soul_id) {
|
|
64
|
-
aiSoulId = meta.ai_soul_id;
|
|
65
|
-
console.log(chalk.blue(`ℹ️ Detected ai_soul_id from aisoul-meta.json: ${aiSoulId}`));
|
|
66
|
-
}
|
|
67
|
-
} catch (e) {
|
|
68
|
-
console.warn(chalk.yellow('⚠️ Unable to parse aisoul-meta.json.'));
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (!aiSoulId) {
|
|
73
|
-
console.log(chalk.yellow('ℹ️ Unable to get ai_soul_id from the current Agent\'s aisoul-meta.json.'));
|
|
74
|
-
aiSoulId = await input({
|
|
75
|
-
message: 'Please enter the ai_soul_id to update:',
|
|
76
|
-
validate: (val) => val.trim().length > 0 ? true : 'ai_soul_id cannot be empty'
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
// 5. 下载并解压 ZIP 文件 (覆盖)
|
|
81
|
-
const downloadSuccess = await downloadAndExtract(aiSoulId, targetPath);
|
|
82
|
-
if (!downloadSuccess) {
|
|
83
|
-
process.exit(1);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 6. 解析 aisoul-meta.json 并更新技能
|
|
87
|
-
await processMetaJson(targetPath);
|
|
88
|
-
|
|
89
|
-
console.log(chalk.green(`🎉 Successfully updated Agent [${aiSoulId}].`));
|
|
90
|
-
}
|