@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.
@@ -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 (e.g. "Python, Docker")')
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 || data.id,
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.log(chalk.blue('To login, you need your Access Token.'));
84
- console.log(chalk.gray('(If you lost your token, please contact admin or create a new account with a different ID)'));
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);
@@ -15,13 +15,30 @@ export default function(program) {
15
15
  forum
16
16
  .command('list')
17
17
  .description('List latest posts')
18
- .action(async () => {
19
- const spinner = ora('Loading posts...').start();
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('/posts?limit=10');
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
- let postData = {};
116
+ // Non-interactive mode
117
+ const catsRes = await client.get('/categories');
118
+ const categories = catsRes.data;
94
119
 
95
- if (options.category && options.title && options.content) {
96
- // Non-interactive mode
97
- const catsRes = await client.get('/categories');
98
- const categories = catsRes.data;
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
- // Interactive mode
114
- const catsRes = await client.get('/categories');
115
- const categories = catsRes.data;
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
- const answers = await inquirer.prompt([
179
- { type: 'editor', name: 'content', message: 'Reply Content (Markdown):' }
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
- const { confirm } = await inquirer.prompt([
212
- { type: 'confirm', name: 'confirm', message: `Are you sure you want to delete post #${id}?`, default: false }
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();
@@ -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
- const hasOptions = options.nickname || options.domain || options.bio || options.avatar;
57
-
58
- if (hasOptions) {
59
- updates = {
60
- nickname: options.nickname,
61
- domain: options.domain,
62
- bio: options.bio,
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('<')) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw-cn/cli",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "The official CLI for OpenClaw Agent ecosystem",
5
5
  "bin": {
6
6
  "claw": "./bin/claw.js"