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.
Files changed (64) hide show
  1. package/README.md +53 -2
  2. package/dist/agent-runner.d.ts.map +1 -1
  3. package/dist/agent-runner.js +23 -5
  4. package/dist/agent-runner.js.map +1 -1
  5. package/dist/cli.js +44 -2
  6. package/dist/cli.js.map +1 -1
  7. package/dist/commands/doctor.d.ts +9 -0
  8. package/dist/commands/doctor.d.ts.map +1 -0
  9. package/dist/commands/doctor.js +211 -0
  10. package/dist/commands/doctor.js.map +1 -0
  11. package/dist/commands/init.d.ts +13 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +260 -0
  14. package/dist/commands/init.js.map +1 -0
  15. package/dist/config-schema.js +1 -1
  16. package/dist/config-schema.js.map +1 -1
  17. package/dist/config.d.ts +2 -0
  18. package/dist/config.d.ts.map +1 -1
  19. package/dist/config.js +3 -0
  20. package/dist/config.js.map +1 -1
  21. package/dist/core/api-client.d.ts +7 -0
  22. package/dist/core/api-client.d.ts.map +1 -1
  23. package/dist/core/api-client.js +85 -39
  24. package/dist/core/api-client.js.map +1 -1
  25. package/dist/core/memory.d.ts +12 -0
  26. package/dist/core/memory.d.ts.map +1 -1
  27. package/dist/core/memory.js +84 -5
  28. package/dist/core/memory.js.map +1 -1
  29. package/dist/db.d.ts +6 -0
  30. package/dist/db.d.ts.map +1 -1
  31. package/dist/db.js +44 -32
  32. package/dist/db.js.map +1 -1
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +187 -54
  35. package/dist/index.js.map +1 -1
  36. package/dist/message-queue.d.ts +3 -1
  37. package/dist/message-queue.d.ts.map +1 -1
  38. package/dist/message-queue.js +26 -9
  39. package/dist/message-queue.js.map +1 -1
  40. package/dist/metrics.d.ts.map +1 -1
  41. package/dist/metrics.js +11 -5
  42. package/dist/metrics.js.map +1 -1
  43. package/dist/plugins/manager.d.ts.map +1 -1
  44. package/dist/plugins/manager.js +18 -2
  45. package/dist/plugins/manager.js.map +1 -1
  46. package/dist/plugins/types.d.ts +1 -0
  47. package/dist/plugins/types.d.ts.map +1 -1
  48. package/dist/plugins/types.js.map +1 -1
  49. package/dist/session-tracker.d.ts +5 -0
  50. package/dist/session-tracker.d.ts.map +1 -1
  51. package/dist/session-tracker.js +23 -8
  52. package/dist/session-tracker.js.map +1 -1
  53. package/dist/utils/rate-limiter.d.ts.map +1 -1
  54. package/dist/utils/rate-limiter.js +3 -0
  55. package/dist/utils/rate-limiter.js.map +1 -1
  56. package/package.json +9 -6
  57. package/plugins/cancel-task/index.ts +1 -1
  58. package/plugins/feishu/index.ts +1 -1
  59. package/plugins/memory/index.ts +2 -2
  60. package/plugins/pause-task/plugin.json +1 -1
  61. package/plugins/register-group/plugin.json +1 -1
  62. package/plugins/resume-task/plugin.json +1 -1
  63. package/plugins/send-message/index.ts +83 -17
  64. 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: string;
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.0.0',
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: ['content']
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
- if (content.length > 10000) {
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
- // 使用上下文提供的 sendMessage 方法发送消息到当前聊天
56
- await context.sendMessage(content);
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
- sent: true
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: `发送消息失败: ${error instanceof Error ? error.message : String(error)}`
135
+ error: `发送失败: ${error instanceof Error ? error.message : String(error)}`
70
136
  };
71
137
  }
72
138
  }
@@ -1,18 +1,54 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Post-install script to create FlashClaw user configuration directories.
4
- * This script runs after npm install to ensure necessary directories exist.
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
- console.log('✓ FlashClaw directories initialized');
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
- // Silently fail if dist is not built yet (e.g., during development)
14
- // The directories will be created when the app runs
15
- if (error && error.code !== 'MODULE_NOT_FOUND') {
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
  }