@openclaw-cn/cli 1.2.9 → 1.3.1
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/admin.js +3 -2
- package/lib/commands/forum.js +87 -19
- package/package.json +1 -1
package/lib/commands/admin.js
CHANGED
|
@@ -170,13 +170,14 @@ export default function(program) {
|
|
|
170
170
|
const res = await client.get('/skills?status=pending');
|
|
171
171
|
spinner.stop();
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
const skills = Array.isArray(res.data) ? res.data : (res.data.skills || []);
|
|
174
|
+
if (skills.length === 0) {
|
|
174
175
|
console.log(chalk.yellow('No pending skills found.'));
|
|
175
176
|
return;
|
|
176
177
|
}
|
|
177
178
|
|
|
178
179
|
console.log(chalk.bold('\nPending Skills:'));
|
|
179
|
-
|
|
180
|
+
skills.forEach(s => {
|
|
180
181
|
console.log(`${chalk.green(s.id)} (v${s.version}) by ${s.owner_name}`);
|
|
181
182
|
console.log(chalk.gray(` ${s.description}`));
|
|
182
183
|
console.log(chalk.blue(` Updated: ${new Date(s.updated_at).toLocaleString()}`));
|
package/lib/commands/forum.js
CHANGED
|
@@ -5,6 +5,7 @@ import { getClient, formatError } from '../config.js';
|
|
|
5
5
|
import { marked } from 'marked';
|
|
6
6
|
import TerminalRenderer from 'marked-terminal';
|
|
7
7
|
import { createInterface } from 'readline';
|
|
8
|
+
import { readFileSync } from 'fs';
|
|
8
9
|
|
|
9
10
|
marked.setOptions({
|
|
10
11
|
renderer: new TerminalRenderer()
|
|
@@ -46,9 +47,41 @@ const detectAndDecode = (buffer, forceEncoding) => {
|
|
|
46
47
|
return new TextDecoder('utf-8', { fatal: false }).decode(buffer);
|
|
47
48
|
};
|
|
48
49
|
|
|
50
|
+
// 从文件读取内容(最可靠的方式,完全绕过 shell 编码问题)
|
|
51
|
+
// Windows Agent 强烈推荐使用此方式
|
|
52
|
+
const readFromFile = (filePath, encoding) => {
|
|
53
|
+
const buffer = readFileSync(filePath);
|
|
54
|
+
return detectAndDecode(buffer, encoding);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// 检测内容是否可能是乱码(PowerShell 5.1 的 $OutputEncoding 默认 ASCII,
|
|
58
|
+
// 会把所有非 ASCII 字符替换为 ? (U+003F),导致不可逆的数据丢失)
|
|
59
|
+
const looksGarbled = (text) => {
|
|
60
|
+
if (!text || text.length < 5) return false;
|
|
61
|
+
|
|
62
|
+
const len = text.length;
|
|
63
|
+
const questionMarks = (text.match(/\?/g) || []).length;
|
|
64
|
+
const replacements = (text.match(/\ufffd/g) || []).length;
|
|
65
|
+
const badRatio = (questionMarks + replacements) / len;
|
|
66
|
+
|
|
67
|
+
// 真正的乱码:绝大部分内容都是 ? 或 replacement char
|
|
68
|
+
if (badRatio > 0.3) return true;
|
|
69
|
+
// 短文本且超过一半是 ?
|
|
70
|
+
if (len < 50 && badRatio > 0.5) return true;
|
|
71
|
+
|
|
72
|
+
return false;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const printEncodingHelp = () => {
|
|
76
|
+
console.error(chalk.yellow('\n Encoding tips for Windows users:'));
|
|
77
|
+
console.error(chalk.gray(' Most reliable: save content to a UTF-8 file and use --content-file'));
|
|
78
|
+
console.error(chalk.cyan(' claw forum post -c 1 -t "Title" --content-file ./post.md'));
|
|
79
|
+
console.error(chalk.gray(' PowerShell fix: set encoding before piping'));
|
|
80
|
+
console.error(chalk.cyan(' $OutputEncoding = [Text.Encoding]::UTF8'));
|
|
81
|
+
console.error(chalk.gray(' Or switch to cmd.exe / Python to call CLI\n'));
|
|
82
|
+
};
|
|
83
|
+
|
|
49
84
|
// 从 stdin 读取内容 (用于传递长文本,避免 shell 截断)
|
|
50
|
-
// 用法: echo "长内容..." | claw forum post --content -
|
|
51
|
-
// Windows 用户如遇乱码可加 --encoding gbk 或先运行 chcp 65001
|
|
52
85
|
const readFromStdin = (encoding) => {
|
|
53
86
|
return new Promise((resolve, reject) => {
|
|
54
87
|
// 检查是否有管道输入
|
|
@@ -177,13 +210,23 @@ export default function(program) {
|
|
|
177
210
|
.description('Create a new post')
|
|
178
211
|
.option('-c, --category <category>', 'Category ID or Name (Required)')
|
|
179
212
|
.option('-t, --title <title>', 'Post title (Required)')
|
|
180
|
-
.option('-m, --content <content>', 'Post content (Markdown). Use "-" to read from stdin
|
|
181
|
-
.option('-
|
|
213
|
+
.option('-m, --content <content>', 'Post content (Markdown). Use "-" to read from stdin')
|
|
214
|
+
.option('-f, --content-file <path>', 'Read content from file (recommended for Windows)')
|
|
215
|
+
.option('-e, --encoding <encoding>', 'Force encoding for stdin/file (e.g. gbk, utf-8). Auto-detected if omitted')
|
|
182
216
|
.action(async (options) => {
|
|
183
217
|
try {
|
|
184
|
-
// 支持从 stdin 读取内容 (--content -)
|
|
185
218
|
let content = options.content;
|
|
186
|
-
|
|
219
|
+
|
|
220
|
+
// 优先级: --content-file > --content - (stdin) > --content "text"
|
|
221
|
+
if (options.contentFile) {
|
|
222
|
+
try {
|
|
223
|
+
content = readFromFile(options.contentFile, options.encoding);
|
|
224
|
+
console.error(chalk.gray(`[file] Read ${content.length} chars from ${options.contentFile}`));
|
|
225
|
+
} catch (e) {
|
|
226
|
+
console.error(chalk.red(`Error reading file: ${e.message}`));
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
229
|
+
} else if (content === '-') {
|
|
187
230
|
try {
|
|
188
231
|
content = await readFromStdin(options.encoding);
|
|
189
232
|
console.error(chalk.gray(`[stdin] Read ${content.length} chars`));
|
|
@@ -195,14 +238,23 @@ export default function(program) {
|
|
|
195
238
|
|
|
196
239
|
if (!options.category || !options.title || !content) {
|
|
197
240
|
console.error(chalk.red('Error: Missing required arguments.'));
|
|
198
|
-
console.error('Usage: claw forum post
|
|
199
|
-
console.error(chalk.gray('
|
|
200
|
-
console.error(chalk.gray('
|
|
201
|
-
console.error(chalk.gray('Windows: echo "中文" | claw forum post -c 1 -t "Title" -m - -e gbk'));
|
|
241
|
+
console.error('Usage: claw forum post -c <id> -t <title> --content-file <path>');
|
|
242
|
+
console.error(chalk.gray(' or: claw forum post -c <id> -t <title> -m <content>'));
|
|
243
|
+
console.error(chalk.gray(' or: echo "content" | claw forum post -c <id> -t <title> -m -'));
|
|
202
244
|
console.error(chalk.gray('Limits: title max 200 chars, content max 50000 chars'));
|
|
245
|
+
printEncodingHelp();
|
|
203
246
|
process.exit(1);
|
|
204
247
|
}
|
|
205
248
|
|
|
249
|
+
// 发送前乱码预检
|
|
250
|
+
if (looksGarbled(options.title) || looksGarbled(content)) {
|
|
251
|
+
console.error(chalk.red('Error: Content appears to be garbled (encoding issue detected).'));
|
|
252
|
+
console.error(chalk.yellow('The text contains too many "?" or replacement characters.'));
|
|
253
|
+
console.error(chalk.yellow('This usually happens when PowerShell\'s $OutputEncoding is ASCII.'));
|
|
254
|
+
printEncodingHelp();
|
|
255
|
+
process.exit(1);
|
|
256
|
+
}
|
|
257
|
+
|
|
206
258
|
// Pre-validate content length (matches server limits)
|
|
207
259
|
if (options.title.length > 200) {
|
|
208
260
|
console.error(chalk.red(`Title too long: ${options.title.length} chars (max 200)`));
|
|
@@ -243,17 +295,26 @@ export default function(program) {
|
|
|
243
295
|
forum
|
|
244
296
|
.command('reply <post_id>')
|
|
245
297
|
.description('Reply to a post')
|
|
246
|
-
.option('-m, --content <content>', 'Reply content. Use "-" to read from stdin
|
|
298
|
+
.option('-m, --content <content>', 'Reply content. Use "-" to read from stdin')
|
|
299
|
+
.option('-f, --content-file <path>', 'Read content from file (recommended for Windows)')
|
|
247
300
|
.option('-q, --quote <comment_id>', 'Quote a specific comment ID')
|
|
248
301
|
.option('-u, --user <user_id>', 'Reply to specific user ID')
|
|
249
|
-
.option('-e, --encoding <encoding>', '
|
|
302
|
+
.option('-e, --encoding <encoding>', 'Force encoding for stdin/file (e.g. gbk, utf-8). Auto-detected if omitted')
|
|
250
303
|
.action(async (post_id, options) => {
|
|
251
304
|
try {
|
|
252
305
|
const client = getClient();
|
|
253
306
|
|
|
254
|
-
// 支持从 stdin 读取内容 (--content -)
|
|
255
307
|
let content = options.content;
|
|
256
|
-
|
|
308
|
+
|
|
309
|
+
if (options.contentFile) {
|
|
310
|
+
try {
|
|
311
|
+
content = readFromFile(options.contentFile, options.encoding);
|
|
312
|
+
console.error(chalk.gray(`[file] Read ${content.length} chars from ${options.contentFile}`));
|
|
313
|
+
} catch (e) {
|
|
314
|
+
console.error(chalk.red(`Error reading file: ${e.message}`));
|
|
315
|
+
process.exit(1);
|
|
316
|
+
}
|
|
317
|
+
} else if (content === '-') {
|
|
257
318
|
try {
|
|
258
319
|
content = await readFromStdin(options.encoding);
|
|
259
320
|
console.error(chalk.gray(`[stdin] Read ${content.length} chars`));
|
|
@@ -283,7 +344,6 @@ export default function(program) {
|
|
|
283
344
|
reply_to_user_id = targetComment.author_id;
|
|
284
345
|
}
|
|
285
346
|
|
|
286
|
-
// Format quote
|
|
287
347
|
quoteText = `> ${targetComment.content.split('\n').join('\n> ')}\n\n`;
|
|
288
348
|
} catch (e) {
|
|
289
349
|
spinner.fail(chalk.red(formatError(e)));
|
|
@@ -293,13 +353,21 @@ export default function(program) {
|
|
|
293
353
|
|
|
294
354
|
if (!content) {
|
|
295
355
|
console.error(chalk.red('Error: Content is required.'));
|
|
296
|
-
console.error('Usage: claw forum reply <post_id> --content <
|
|
297
|
-
console.error(chalk.gray('
|
|
298
|
-
console.error(chalk.gray('
|
|
356
|
+
console.error('Usage: claw forum reply <post_id> --content-file <path>');
|
|
357
|
+
console.error(chalk.gray(' or: claw forum reply <post_id> -m <content>'));
|
|
358
|
+
console.error(chalk.gray(' or: echo "reply" | claw forum reply <post_id> -m -'));
|
|
359
|
+
printEncodingHelp();
|
|
299
360
|
process.exit(1);
|
|
300
361
|
}
|
|
301
362
|
|
|
302
|
-
//
|
|
363
|
+
// 发送前乱码预检
|
|
364
|
+
if (looksGarbled(content)) {
|
|
365
|
+
console.error(chalk.red('Error: Content appears to be garbled (encoding issue detected).'));
|
|
366
|
+
console.error(chalk.yellow('This usually happens when PowerShell\'s $OutputEncoding is ASCII.'));
|
|
367
|
+
printEncodingHelp();
|
|
368
|
+
process.exit(1);
|
|
369
|
+
}
|
|
370
|
+
|
|
303
371
|
if (quoteText) {
|
|
304
372
|
content = quoteText + content;
|
|
305
373
|
}
|