flashclaw 1.0.1 → 1.3.0
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 +53 -2
- package/dist/agent-runner.d.ts.map +1 -1
- package/dist/agent-runner.js +23 -5
- package/dist/agent-runner.js.map +1 -1
- package/dist/cli.js +44 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +9 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +211 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +260 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/config-schema.js +1 -1
- package/dist/config-schema.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/core/api-client.d.ts +7 -0
- package/dist/core/api-client.d.ts.map +1 -1
- package/dist/core/api-client.js +85 -39
- package/dist/core/api-client.js.map +1 -1
- package/dist/core/memory.d.ts +12 -0
- package/dist/core/memory.d.ts.map +1 -1
- package/dist/core/memory.js +84 -5
- package/dist/core/memory.js.map +1 -1
- package/dist/db.d.ts +6 -0
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +44 -32
- package/dist/db.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +187 -54
- package/dist/index.js.map +1 -1
- package/dist/message-queue.d.ts +3 -1
- package/dist/message-queue.d.ts.map +1 -1
- package/dist/message-queue.js +26 -9
- package/dist/message-queue.js.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +11 -5
- package/dist/metrics.js.map +1 -1
- package/dist/plugins/manager.d.ts.map +1 -1
- package/dist/plugins/manager.js +18 -2
- package/dist/plugins/manager.js.map +1 -1
- package/dist/plugins/types.d.ts +1 -0
- package/dist/plugins/types.d.ts.map +1 -1
- package/dist/plugins/types.js.map +1 -1
- package/dist/session-tracker.d.ts +5 -0
- package/dist/session-tracker.d.ts.map +1 -1
- package/dist/session-tracker.js +23 -8
- package/dist/session-tracker.js.map +1 -1
- package/dist/utils/rate-limiter.d.ts.map +1 -1
- package/dist/utils/rate-limiter.js +3 -0
- package/dist/utils/rate-limiter.js.map +1 -1
- package/package.json +9 -6
- package/plugins/cancel-task/index.ts +1 -1
- package/plugins/feishu/index.ts +1 -1
- package/plugins/memory/index.ts +2 -2
- package/plugins/pause-task/plugin.json +1 -1
- package/plugins/register-group/plugin.json +1 -1
- package/plugins/resume-task/plugin.json +1 -1
- package/plugins/send-message/index.ts +83 -17
- package/scripts/postinstall.js +43 -7
|
@@ -1,50 +1,82 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FlashClaw 插件 - 发送消息
|
|
3
|
-
* 允许 AI Agent
|
|
3
|
+
* 允许 AI Agent 主动发送消息和图片到聊天
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { ToolPlugin, ToolContext, ToolResult } from '../../src/plugins/types.js';
|
|
7
|
+
import { tmpdir } from 'os';
|
|
8
|
+
import { join } from 'path';
|
|
9
|
+
import { readFileSync, existsSync } from 'fs';
|
|
10
|
+
|
|
11
|
+
// 最近截图的临时文件路径(与 browser-control 插件共享)
|
|
12
|
+
const LATEST_SCREENSHOT_PATH = join(tmpdir(), 'flashclaw-latest-screenshot.txt');
|
|
13
|
+
|
|
14
|
+
/** 从临时文件读取最近截图 */
|
|
15
|
+
function getLatestScreenshot(): string | null {
|
|
16
|
+
if (!existsSync(LATEST_SCREENSHOT_PATH)) return null;
|
|
17
|
+
try {
|
|
18
|
+
return readFileSync(LATEST_SCREENSHOT_PATH, 'utf-8');
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
7
23
|
|
|
8
24
|
/**
|
|
9
25
|
* 发送消息参数
|
|
10
26
|
*/
|
|
11
27
|
interface SendMessageParams {
|
|
12
28
|
/** 要发送的消息内容 */
|
|
13
|
-
content
|
|
29
|
+
content?: string;
|
|
30
|
+
/** 要发送的图片(base64 或 data URL 格式) */
|
|
31
|
+
image?: string;
|
|
32
|
+
/** 图片说明文字 */
|
|
33
|
+
caption?: string;
|
|
14
34
|
}
|
|
15
35
|
|
|
16
36
|
const plugin: ToolPlugin = {
|
|
17
37
|
name: 'send_message',
|
|
18
|
-
version: '1.
|
|
19
|
-
description: '
|
|
38
|
+
version: '1.1.0',
|
|
39
|
+
description: '发送消息或图片到当前聊天',
|
|
20
40
|
|
|
21
41
|
schema: {
|
|
22
42
|
name: 'send_message',
|
|
23
|
-
description: '
|
|
43
|
+
description: '发送消息或图片到当前聊天。用于主动通知用户、回复消息、发送执行结果或分享截图等图片。',
|
|
24
44
|
input_schema: {
|
|
25
45
|
type: 'object',
|
|
26
46
|
properties: {
|
|
27
47
|
content: {
|
|
28
48
|
type: 'string',
|
|
29
|
-
description: '
|
|
49
|
+
description: '要发送的文本消息内容'
|
|
50
|
+
},
|
|
51
|
+
image: {
|
|
52
|
+
type: 'string',
|
|
53
|
+
description: '要发送的图片。支持:1) "latest_screenshot" - 发送最近的浏览器截图;2) base64 编码字符串;3) data URL 格式。推荐使用 "latest_screenshot" 发送 browser_action screenshot 的截图。'
|
|
54
|
+
},
|
|
55
|
+
caption: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
description: '图片说明文字(仅在发送图片时有效)'
|
|
30
58
|
}
|
|
31
59
|
},
|
|
32
|
-
required: [
|
|
60
|
+
required: [] // content 和 image 至少需要一个
|
|
33
61
|
}
|
|
34
62
|
},
|
|
35
63
|
|
|
36
64
|
async execute(params: unknown, context: ToolContext): Promise<ToolResult> {
|
|
37
|
-
const { content } = params as SendMessageParams;
|
|
65
|
+
const { content, image, caption } = params as SendMessageParams;
|
|
66
|
+
|
|
67
|
+
// 验证至少有一个内容
|
|
68
|
+
const hasContent = content && typeof content === 'string' && content.trim().length > 0;
|
|
69
|
+
const hasImage = image && typeof image === 'string' && image.length > 0;
|
|
38
70
|
|
|
39
|
-
|
|
40
|
-
if (!content || typeof content !== 'string') {
|
|
71
|
+
if (!hasContent && !hasImage) {
|
|
41
72
|
return {
|
|
42
73
|
success: false,
|
|
43
|
-
error: '
|
|
74
|
+
error: '消息内容和图片至少需要提供一个'
|
|
44
75
|
};
|
|
45
76
|
}
|
|
46
77
|
|
|
47
|
-
|
|
78
|
+
// 验证文本长度
|
|
79
|
+
if (hasContent && content.length > 10000) {
|
|
48
80
|
return {
|
|
49
81
|
success: false,
|
|
50
82
|
error: '消息内容过长,最大支持 10000 字符'
|
|
@@ -52,21 +84,55 @@ const plugin: ToolPlugin = {
|
|
|
52
84
|
}
|
|
53
85
|
|
|
54
86
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
87
|
+
const results: { text?: boolean; image?: boolean } = {};
|
|
88
|
+
|
|
89
|
+
// 发送图片(如果有)
|
|
90
|
+
if (hasImage) {
|
|
91
|
+
let imageData = image;
|
|
92
|
+
|
|
93
|
+
// 处理特殊引用:latest_screenshot
|
|
94
|
+
if (image === 'latest_screenshot') {
|
|
95
|
+
const screenshot = getLatestScreenshot();
|
|
96
|
+
if (screenshot) {
|
|
97
|
+
imageData = `data:image/png;base64,${screenshot}`;
|
|
98
|
+
} else {
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
error: '没有可用的截图。请先使用 browser_action screenshot 截图。'
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
} else if (!image.startsWith('data:')) {
|
|
105
|
+
// 处理图片格式:如果是纯 base64,转换为 data URL
|
|
106
|
+
imageData = `data:image/png;base64,${image}`;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await context.sendImage(imageData, caption);
|
|
110
|
+
results.image = true;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// 发送文本消息(如果有,且不是作为图片说明)
|
|
114
|
+
if (hasContent && !hasImage) {
|
|
115
|
+
await context.sendMessage(content);
|
|
116
|
+
results.text = true;
|
|
117
|
+
} else if (hasContent && hasImage && !caption) {
|
|
118
|
+
// 如果同时有文本和图片但没指定 caption,文本单独发送
|
|
119
|
+
await context.sendMessage(content);
|
|
120
|
+
results.text = true;
|
|
121
|
+
}
|
|
57
122
|
|
|
58
123
|
return {
|
|
59
124
|
success: true,
|
|
60
125
|
data: {
|
|
61
126
|
chatId: context.chatId,
|
|
62
|
-
contentLength: content.length,
|
|
63
|
-
|
|
127
|
+
contentLength: hasContent ? content.length : 0,
|
|
128
|
+
hasImage: hasImage,
|
|
129
|
+
sent: results
|
|
64
130
|
}
|
|
65
131
|
};
|
|
66
132
|
} catch (error) {
|
|
67
133
|
return {
|
|
68
134
|
success: false,
|
|
69
|
-
error:
|
|
135
|
+
error: `发送失败: ${error instanceof Error ? error.message : String(error)}`
|
|
70
136
|
};
|
|
71
137
|
}
|
|
72
138
|
}
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,18 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Post-install script
|
|
4
|
-
*
|
|
3
|
+
* Post-install script for FlashClaw.
|
|
4
|
+
*
|
|
5
|
+
* - Creates necessary user directories (~/.flashclaw/)
|
|
6
|
+
* - Shows first-time installation guide if .env doesn't exist
|
|
5
7
|
*/
|
|
6
8
|
|
|
9
|
+
import { existsSync } from 'fs';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { homedir } from 'os';
|
|
12
|
+
|
|
7
13
|
async function run() {
|
|
8
14
|
try {
|
|
9
|
-
const { ensureDirectories } = await import('../dist/paths.js');
|
|
15
|
+
const { ensureDirectories, paths } = await import('../dist/paths.js');
|
|
10
16
|
ensureDirectories();
|
|
11
|
-
|
|
17
|
+
|
|
18
|
+
const envPath = paths.env();
|
|
19
|
+
const isFirstTime = !existsSync(envPath);
|
|
20
|
+
|
|
21
|
+
if (isFirstTime) {
|
|
22
|
+
// 首次安装引导
|
|
23
|
+
console.log('');
|
|
24
|
+
console.log(' ⚡ FlashClaw 安装成功!');
|
|
25
|
+
console.log('');
|
|
26
|
+
console.log(' 下一步:');
|
|
27
|
+
console.log(' 1. flashclaw init 交互式配置(推荐)');
|
|
28
|
+
console.log(' 2. flashclaw start 启动服务');
|
|
29
|
+
console.log(' 3. flashclaw doctor 检查运行环境');
|
|
30
|
+
console.log('');
|
|
31
|
+
console.log(' 文档: https://github.com/GuLu9527/flashclaw');
|
|
32
|
+
console.log('');
|
|
33
|
+
} else {
|
|
34
|
+
console.log('✓ FlashClaw directories initialized');
|
|
35
|
+
}
|
|
12
36
|
} catch (error) {
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
|
|
37
|
+
// dist 未编译时(开发环境 clone 后首次 npm install),尝试最小化创建
|
|
38
|
+
if (error && error.code === 'MODULE_NOT_FOUND') {
|
|
39
|
+
try {
|
|
40
|
+
const flashclawHome = process.env.FLASHCLAW_HOME || join(homedir(), '.flashclaw');
|
|
41
|
+
const dirs = ['config', 'data', 'logs', 'plugins', 'groups'];
|
|
42
|
+
const { mkdirSync } = await import('fs');
|
|
43
|
+
for (const dir of dirs) {
|
|
44
|
+
const p = join(flashclawHome, dir);
|
|
45
|
+
if (!existsSync(p)) mkdirSync(p, { recursive: true });
|
|
46
|
+
}
|
|
47
|
+
console.log('✓ FlashClaw directories initialized (dev mode)');
|
|
48
|
+
} catch {
|
|
49
|
+
// 完全静默失败,目录会在运行时创建
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
16
52
|
console.warn('Warning: Could not initialize FlashClaw directories:', error.message);
|
|
17
53
|
}
|
|
18
54
|
}
|