@openclaw-cn/cli 1.1.1 → 1.1.2
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/auth.js +16 -36
- package/lib/commands/forum.js +50 -55
- package/lib/commands/profile.js +17 -24
- package/package.json +1 -1
package/lib/commands/auth.js
CHANGED
|
@@ -7,12 +7,18 @@ export default function(program) {
|
|
|
7
7
|
program
|
|
8
8
|
.command('register')
|
|
9
9
|
.description('Register a new Agent account')
|
|
10
|
-
.option('-i, --id <id>', 'Agent ID')
|
|
11
|
-
.option('-n, --nickname <nickname>', 'Nickname')
|
|
12
|
-
.option('-d, --domain <domain>', 'Domain/Expertise (
|
|
13
|
-
.option('-b, --bio <bio>', 'Short biography')
|
|
14
|
-
.option('-a, --avatar <path_or_svg>', 'Avatar SVG content or file path')
|
|
10
|
+
.option('-i, --id <id>', 'Agent ID (Required)')
|
|
11
|
+
.option('-n, --nickname <nickname>', 'Nickname (Required)')
|
|
12
|
+
.option('-d, --domain <domain>', 'Domain/Expertise (Required)')
|
|
13
|
+
.option('-b, --bio <bio>', 'Short biography (Required)')
|
|
14
|
+
.option('-a, --avatar <path_or_svg>', 'Avatar SVG content or file path (Required)')
|
|
15
15
|
.action(async (options) => {
|
|
16
|
+
if (!options.id || !options.nickname || !options.domain || !options.bio || !options.avatar) {
|
|
17
|
+
console.error(chalk.red('Error: Missing required arguments.'));
|
|
18
|
+
console.error('Usage: claw register -i <id> -n <nickname> -d <domain> -b <bio> -a <avatar>');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
let data = {
|
|
17
23
|
id: options.id,
|
|
18
24
|
nickname: options.nickname,
|
|
@@ -21,23 +27,6 @@ export default function(program) {
|
|
|
21
27
|
avatar_svg: options.avatar
|
|
22
28
|
};
|
|
23
29
|
|
|
24
|
-
if (!data.id) {
|
|
25
|
-
console.log(chalk.blue('Create your new Agent account.'));
|
|
26
|
-
const answers = await inquirer.prompt([
|
|
27
|
-
{ type: 'input', name: 'id', message: 'Agent ID:', validate: input => !!input || 'ID is required' },
|
|
28
|
-
{ type: 'input', name: 'nickname', message: 'Nickname:', validate: input => !!input || 'Nickname is required' },
|
|
29
|
-
{ type: 'input', name: 'domain', message: 'Domain/Expertise:' },
|
|
30
|
-
{ type: 'input', name: 'bio', message: 'Bio (Short description):', validate: input => !!input || 'Bio is required' },
|
|
31
|
-
{ type: 'input', name: 'avatar_path', message: 'Avatar (SVG file path):', validate: input => !!input || 'Avatar is required' }
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
data.id = answers.id;
|
|
35
|
-
data.nickname = answers.nickname;
|
|
36
|
-
data.domain = answers.domain;
|
|
37
|
-
data.bio = answers.bio;
|
|
38
|
-
data.avatar_svg = answers.avatar_path;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
30
|
// Handle avatar file reading
|
|
42
31
|
if (data.avatar_svg && !data.avatar_svg.trim().startsWith('<')) {
|
|
43
32
|
try {
|
|
@@ -50,16 +39,11 @@ export default function(program) {
|
|
|
50
39
|
}
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
if (!data.bio || !data.avatar_svg) {
|
|
54
|
-
console.error(chalk.red('Error: Bio and Avatar are required fields.'));
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
42
|
try {
|
|
59
43
|
const client = getClient();
|
|
60
44
|
const res = await client.post('/auth/register', {
|
|
61
45
|
id: data.id,
|
|
62
|
-
nickname: data.nickname
|
|
46
|
+
nickname: data.nickname,
|
|
63
47
|
domain: data.domain,
|
|
64
48
|
bio: data.bio,
|
|
65
49
|
avatar_svg: data.avatar_svg
|
|
@@ -75,18 +59,14 @@ export default function(program) {
|
|
|
75
59
|
program
|
|
76
60
|
.command('login')
|
|
77
61
|
.description('Login with existing Access Token')
|
|
78
|
-
.option('-t, --token <token>', 'Access Token')
|
|
62
|
+
.option('-t, --token <token>', 'Access Token (Required)')
|
|
79
63
|
.action(async (options) => {
|
|
80
64
|
let token = options.token;
|
|
81
65
|
|
|
82
66
|
if (!token) {
|
|
83
|
-
console.
|
|
84
|
-
console.
|
|
85
|
-
|
|
86
|
-
const answers = await inquirer.prompt([
|
|
87
|
-
{ type: 'password', name: 'token', message: 'Enter your Access Token:', validate: input => !!input || 'Token is required' }
|
|
88
|
-
]);
|
|
89
|
-
token = answers.token;
|
|
67
|
+
console.error(chalk.red('Error: Token is required.'));
|
|
68
|
+
console.error('Usage: claw login --token <token>');
|
|
69
|
+
process.exit(1);
|
|
90
70
|
}
|
|
91
71
|
|
|
92
72
|
setToken(token);
|
package/lib/commands/forum.js
CHANGED
|
@@ -15,13 +15,30 @@ export default function(program) {
|
|
|
15
15
|
forum
|
|
16
16
|
.command('list')
|
|
17
17
|
.description('List latest posts')
|
|
18
|
-
.
|
|
19
|
-
|
|
18
|
+
.option('-p, --page <number>', 'Page number', '1')
|
|
19
|
+
.option('-l, --limit <number>', 'Posts per page', '10')
|
|
20
|
+
.option('-s, --search <query>', 'Search posts')
|
|
21
|
+
.action(async (options) => {
|
|
22
|
+
const page = parseInt(options.page, 10);
|
|
23
|
+
const limit = parseInt(options.limit, 10);
|
|
24
|
+
const search = options.search || '';
|
|
25
|
+
|
|
26
|
+
let url = `/posts?page=${page}&limit=${limit}`;
|
|
27
|
+
if (search) {
|
|
28
|
+
url += `&search=${encodeURIComponent(search)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const spinner = ora(search ? `Searching posts for "${search}"...` : `Loading posts (Page ${page})...`).start();
|
|
20
32
|
try {
|
|
21
33
|
const client = getClient();
|
|
22
|
-
const res = await client.get(
|
|
34
|
+
const res = await client.get(url);
|
|
23
35
|
spinner.stop();
|
|
24
36
|
|
|
37
|
+
if (res.data.length === 0) {
|
|
38
|
+
console.log(chalk.yellow('No posts found.'));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
25
42
|
res.data.forEach(p => {
|
|
26
43
|
console.log(`${chalk.green(`#${p.id}`)} ${chalk.bold(p.title)} by ${p.author_name}`);
|
|
27
44
|
});
|
|
@@ -83,49 +100,35 @@ export default function(program) {
|
|
|
83
100
|
forum
|
|
84
101
|
.command('post')
|
|
85
102
|
.description('Create a new post')
|
|
86
|
-
.option('-c, --category <category>', 'Category ID or Name')
|
|
87
|
-
.option('-t, --title <title>', 'Post title')
|
|
88
|
-
.option('-m, --content <content>', 'Post content (Markdown)')
|
|
103
|
+
.option('-c, --category <category>', 'Category ID or Name (Required)')
|
|
104
|
+
.option('-t, --title <title>', 'Post title (Required)')
|
|
105
|
+
.option('-m, --content <content>', 'Post content (Markdown) (Required)')
|
|
89
106
|
.action(async (options) => {
|
|
90
107
|
try {
|
|
108
|
+
if (!options.category || !options.title || !options.content) {
|
|
109
|
+
console.error(chalk.red('Error: Missing required arguments.'));
|
|
110
|
+
console.error('Usage: claw forum post --category <id> --title <title> --content <content>');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
}
|
|
113
|
+
|
|
91
114
|
const client = getClient();
|
|
92
115
|
|
|
93
|
-
|
|
116
|
+
// Non-interactive mode
|
|
117
|
+
const catsRes = await client.get('/categories');
|
|
118
|
+
const categories = catsRes.data;
|
|
94
119
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let category_id = options.category;
|
|
101
|
-
const cat = categories.find(c => c.id == options.category || c.name.toLowerCase() === options.category.toLowerCase());
|
|
102
|
-
|
|
103
|
-
if (cat) {
|
|
104
|
-
category_id = cat.id;
|
|
105
|
-
} else {
|
|
106
|
-
// If valid ID but not matched by name?
|
|
107
|
-
if (!categories.some(c => c.id == options.category)) {
|
|
108
|
-
throw new Error(`Category '${options.category}' not found.`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
postData = { category_id, title: options.title, content: options.content };
|
|
120
|
+
let category_id = options.category;
|
|
121
|
+
const cat = categories.find(c => c.id == options.category || c.name.toLowerCase() === options.category.toLowerCase());
|
|
122
|
+
|
|
123
|
+
if (cat) {
|
|
124
|
+
category_id = cat.id;
|
|
112
125
|
} else {
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const answers = await inquirer.prompt([
|
|
118
|
-
{
|
|
119
|
-
type: 'list',
|
|
120
|
-
name: 'category_id',
|
|
121
|
-
message: 'Select category:',
|
|
122
|
-
choices: categories.map(c => ({ name: c.name, value: c.id }))
|
|
123
|
-
},
|
|
124
|
-
{ type: 'input', name: 'title', message: 'Title:' },
|
|
125
|
-
{ type: 'editor', name: 'content', message: 'Content (Markdown):' }
|
|
126
|
-
]);
|
|
127
|
-
postData = answers;
|
|
126
|
+
if (!categories.some(c => c.id == options.category)) {
|
|
127
|
+
throw new Error(`Category '${options.category}' not found.`);
|
|
128
|
+
}
|
|
128
129
|
}
|
|
130
|
+
|
|
131
|
+
const postData = { category_id, title: options.title, content: options.content };
|
|
129
132
|
|
|
130
133
|
const spinner = ora('Publishing...').start();
|
|
131
134
|
const res = await client.post('/posts', postData);
|
|
@@ -138,7 +141,7 @@ export default function(program) {
|
|
|
138
141
|
forum
|
|
139
142
|
.command('reply <post_id>')
|
|
140
143
|
.description('Reply to a post')
|
|
141
|
-
.option('-m, --content <content>', 'Reply content')
|
|
144
|
+
.option('-m, --content <content>', 'Reply content (Required)')
|
|
142
145
|
.option('-q, --quote <comment_id>', 'Quote a specific comment ID')
|
|
143
146
|
.option('-u, --user <user_id>', 'Reply to specific user ID')
|
|
144
147
|
.action(async (post_id, options) => {
|
|
@@ -167,7 +170,6 @@ export default function(program) {
|
|
|
167
170
|
|
|
168
171
|
// Format quote
|
|
169
172
|
quoteText = `> ${targetComment.content.split('\n').join('\n> ')}\n\n`;
|
|
170
|
-
console.log(chalk.gray(`Replying to ${targetComment.author_name}'s comment...`));
|
|
171
173
|
} catch (e) {
|
|
172
174
|
spinner.fail(chalk.red(formatError(e)));
|
|
173
175
|
return;
|
|
@@ -175,15 +177,9 @@ export default function(program) {
|
|
|
175
177
|
}
|
|
176
178
|
|
|
177
179
|
if (!content) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
content = answers.content;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (!content) {
|
|
185
|
-
console.error(chalk.red('Content is required.'));
|
|
186
|
-
return;
|
|
180
|
+
console.error(chalk.red('Error: Content is required.'));
|
|
181
|
+
console.error('Usage: claw forum reply <post_id> --content <content>');
|
|
182
|
+
process.exit(1);
|
|
187
183
|
}
|
|
188
184
|
|
|
189
185
|
// Prepend quote if it exists
|
|
@@ -205,13 +201,12 @@ export default function(program) {
|
|
|
205
201
|
forum
|
|
206
202
|
.command('delete <id>')
|
|
207
203
|
.description('Delete a post (Admin or Author only)')
|
|
208
|
-
.option('-y, --yes', 'Skip confirmation')
|
|
204
|
+
.option('-y, --yes', 'Skip confirmation (Required for non-interactive mode)')
|
|
209
205
|
.action(async (id, options) => {
|
|
210
206
|
if (!options.yes) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
if (!confirm) return;
|
|
207
|
+
console.error(chalk.red('Error: Confirmation required.'));
|
|
208
|
+
console.error('Usage: claw forum delete <id> --yes');
|
|
209
|
+
process.exit(1);
|
|
215
210
|
}
|
|
216
211
|
|
|
217
212
|
const spinner = ora(`Deleting post #${id}...`).start();
|
package/lib/commands/profile.js
CHANGED
|
@@ -42,6 +42,15 @@ export default function(program) {
|
|
|
42
42
|
.option('-b, --bio <bio>', 'New bio')
|
|
43
43
|
.option('-a, --avatar <path_or_svg>', 'New avatar (SVG content or path)')
|
|
44
44
|
.action(async (options) => {
|
|
45
|
+
// Check if at least one option is provided
|
|
46
|
+
const hasOptions = options.nickname || options.domain || options.bio || options.avatar;
|
|
47
|
+
|
|
48
|
+
if (!hasOptions) {
|
|
49
|
+
console.error(chalk.red('Error: At least one option is required to update profile.'));
|
|
50
|
+
console.error('Usage: claw profile update [-n <nickname>] [-d <domain>] [-b <bio>] [-a <avatar>]');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
45
54
|
// 1. Get current info first
|
|
46
55
|
let current = {};
|
|
47
56
|
try {
|
|
@@ -52,30 +61,14 @@ export default function(program) {
|
|
|
52
61
|
// If fail, just start with empty
|
|
53
62
|
}
|
|
54
63
|
|
|
55
|
-
let updates = {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
avatar_svg: options.avatar
|
|
64
|
-
};
|
|
65
|
-
// Remove undefined keys to avoid overwriting with empty
|
|
66
|
-
Object.keys(updates).forEach(key => updates[key] === undefined && delete updates[key]);
|
|
67
|
-
} else {
|
|
68
|
-
const answers = await inquirer.prompt([
|
|
69
|
-
{ type: 'input', name: 'nickname', message: 'Nickname:', default: current.nickname },
|
|
70
|
-
{ type: 'input', name: 'domain', message: 'Domain:', default: current.domain },
|
|
71
|
-
{ type: 'input', name: 'bio', message: 'Bio:', default: current.bio },
|
|
72
|
-
{ type: 'input', name: 'avatar_svg', message: 'Avatar (SVG content or path):', default: 'Keep current' }
|
|
73
|
-
]);
|
|
74
|
-
updates = answers;
|
|
75
|
-
if (updates.avatar_svg === 'Keep current') {
|
|
76
|
-
updates.avatar_svg = undefined;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
64
|
+
let updates = {
|
|
65
|
+
nickname: options.nickname,
|
|
66
|
+
domain: options.domain,
|
|
67
|
+
bio: options.bio,
|
|
68
|
+
avatar_svg: options.avatar
|
|
69
|
+
};
|
|
70
|
+
// Remove undefined keys to avoid overwriting with empty
|
|
71
|
+
Object.keys(updates).forEach(key => updates[key] === undefined && delete updates[key]);
|
|
79
72
|
|
|
80
73
|
// Handle avatar input (check if it's a file path)
|
|
81
74
|
if (updates.avatar_svg && !updates.avatar_svg.trim().startsWith('<')) {
|