@licity/qclaw-local-connector 1.3.0 → 1.3.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/README.md +55 -13
- package/index.js +51 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,36 +16,70 @@
|
|
|
16
16
|
|
|
17
17
|
### 方式一:npm 全局安装(推荐)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
**第一步:安装 Node.js**
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
没有 Node.js 的需要先安装它(npm 命令附带在 Node.js 里)。
|
|
22
|
+
|
|
23
|
+
1. 打开浏览器,访问 [nodejs.org](https://nodejs.org)
|
|
24
|
+
2. 点击显示"LTS"的大按钮下载安装包(Windows 下载 `.msi` 文件)
|
|
25
|
+
3. 下载完成后双击运行,一直点"Next"直到安装完成,保持所有默认选项
|
|
26
|
+
4. 安装完后打开终端(见下一步),输入 `node -v`,能看到版本号说明安装成功
|
|
27
|
+
|
|
28
|
+
**如何打开终端:**
|
|
29
|
+
- **Windows 11/10**:按 `Win + X`,选择"终端"或"PowerShell";或按 `Win + R`,输入 `cmd` 回车
|
|
30
|
+
- **macOS**:按 `Cmd + 空格`,搜索"Terminal"打开
|
|
31
|
+
- **或者**:在文件夹空白处右键,选择"在此处打开终端"/"在终端中打开"
|
|
22
32
|
|
|
23
33
|
**第二步:全局安装连接器**
|
|
24
34
|
|
|
35
|
+
在终端里输入以下命令并回车:
|
|
36
|
+
|
|
25
37
|
```bash
|
|
26
38
|
npm install -g @licity/qclaw-local-connector
|
|
27
39
|
```
|
|
28
40
|
|
|
29
|
-
|
|
41
|
+
等待安装完成,看到 `added X packages` 字样即成功。安装过程中出现 `WARN` 字样是正常的,不影响使用。
|
|
42
|
+
|
|
43
|
+
> **⚠️ Windows PowerShell 提示"禁止运行脚本"怎么办?**
|
|
44
|
+
>
|
|
45
|
+
> 如果出现以下错误:
|
|
46
|
+
> ```
|
|
47
|
+
> npm : 无法加载文件 ...npm.ps1,因为在此系统上禁止运行脚本
|
|
48
|
+
> ```
|
|
49
|
+
> 原因是 Windows PowerShell 默认禁止运行第三方脚本。有两个解决方案:
|
|
50
|
+
>
|
|
51
|
+
> **方案 A(推荐):改用 cmd 而不是 PowerShell**
|
|
52
|
+
> - 按 `Win + R`,输入 `cmd` 回车,打开命令提示符(不是 PowerShell)
|
|
53
|
+
> - 在 cmd 里重新运行 `npm install -g @licity/qclaw-local-connector`
|
|
54
|
+
>
|
|
55
|
+
> **方案 B:修改 PowerShell 执行策略(需要管理员权限)**
|
|
56
|
+
> - 右键点击开始菜单 → 选择"Windows PowerShell(管理员)"或"终端(管理员)"
|
|
57
|
+
> - 在管理员 PowerShell 里输入:
|
|
58
|
+
> ```powershell
|
|
59
|
+
> Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
|
|
60
|
+
> ```
|
|
61
|
+
> - 按 `Y` 确认,然后重新运行安装命令
|
|
30
62
|
|
|
31
63
|
**第三步:创建工作目录**
|
|
32
64
|
|
|
33
|
-
|
|
65
|
+
在电脑上选一个记得住的文件夹作为工作目录。例如在桌面新建一个 `licity` 文件夹。
|
|
34
66
|
|
|
35
|
-
> ⚠️ **重要**:不要在 npm
|
|
67
|
+
> ⚠️ **重要**:不要在 npm 全局安装目录里直接运行(路径包含 `node_modules/@licity`)。请始终在你自己建的工作目录里运行。
|
|
36
68
|
|
|
37
|
-
|
|
69
|
+
**第四步:初次运行,自动生成 `.env` 配置文件**
|
|
38
70
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
**第五步:启动连接器**
|
|
42
|
-
|
|
43
|
-
在工作目录里右键"在此处打开终端",然后执行:
|
|
71
|
+
在工作目录(例如桌面的 `licity` 文件夹)里右键 → 在此处打开终端,然后运行:
|
|
44
72
|
|
|
45
73
|
```bash
|
|
46
74
|
licity-connector
|
|
47
75
|
```
|
|
48
76
|
|
|
77
|
+
**如果是第一次运行且当前目录没有 `.env` 文件,连接器会自动在当前目录生成一个 `.env` 模板文件,然后退出并提示你填写。**
|
|
78
|
+
|
|
79
|
+
用文本编辑器打开自动生成的 `.env` 文件,按第 4 步说明填写必填项,保存后重新运行 `licity-connector` 即可。
|
|
80
|
+
|
|
81
|
+
不需要手动创建 `.env` 文件 — 第一次运行会帮你生成。
|
|
82
|
+
|
|
49
83
|
### 方式二:本地克隆 / 下载后运行
|
|
50
84
|
|
|
51
85
|
进入连接器目录,执行:
|
|
@@ -162,9 +196,17 @@ npm install && npm run quickstart
|
|
|
162
196
|
|
|
163
197
|
### 第 4 步:第一次启动时补全 `.env`
|
|
164
198
|
|
|
165
|
-
|
|
199
|
+
**方式一(npm 全局安装)用户:**
|
|
200
|
+
|
|
201
|
+
不需要手动创建 `.env` 文件。第一次在工作目录运行 `licity-connector` 时,如果目录里没有 `.env`,程序会**自动生成**一份模板并退出,提示你填写。
|
|
202
|
+
|
|
203
|
+
然后用记事本或任何文本编辑器打开生成的 `.env` 文件,按下面说明填写必填项,保存后重新运行 `licity-connector`。
|
|
204
|
+
|
|
205
|
+
**方式二(本地克隆/下载)用户:**
|
|
206
|
+
|
|
207
|
+
首次运行 `npm run quickstart` 时,安装助手会自动从 `.env.example` 生成一份 `.env` 模板,直接编辑那个文件即可。
|
|
166
208
|
|
|
167
|
-
|
|
209
|
+
---
|
|
168
210
|
|
|
169
211
|
`.env` 文件的完整示例如下。复制粘贴后,按 **【必填/可选/勿改】** 说明逐行确认:
|
|
170
212
|
|
package/index.js
CHANGED
|
@@ -449,6 +449,52 @@ function extractAgentMedia(result) {
|
|
|
449
449
|
return null;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
// 从 AI 回复文本中提取本地文件路径,当 agent 没有结构化附件输出时作为兜底
|
|
453
|
+
function extractLocalFileFromReply(replyText) {
|
|
454
|
+
if (!replyText) return null;
|
|
455
|
+
|
|
456
|
+
// 匹配 Windows 绝对路径(含中文路径)和 Unix 绝对路径
|
|
457
|
+
const patterns = [
|
|
458
|
+
// Windows: C:\path\file.ext 或 C:/path/file.ext(包含中文字符和空格)
|
|
459
|
+
/[A-Za-z]:[\\\/][^\s`'",。!?\]]{3,260}\.[a-zA-Z0-9]{1,10}/g,
|
|
460
|
+
// Unix: /Users/path/file.ext
|
|
461
|
+
/(?:\/(?:[^\s`'",。!?\]]+\/)+[^\s`'",。!?\]]+\.[a-zA-Z0-9]{1,10})/g,
|
|
462
|
+
];
|
|
463
|
+
|
|
464
|
+
const candidates = [];
|
|
465
|
+
for (const pattern of patterns) {
|
|
466
|
+
const matches = replyText.matchAll(pattern);
|
|
467
|
+
for (const m of matches) {
|
|
468
|
+
// 去掉首尾的引号、反引号
|
|
469
|
+
const candidate = m[0].trim().replace(/^[`'"【]|[`'"】,,。!?]$/g, '');
|
|
470
|
+
if (candidate.length > 3) candidates.push(candidate);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// 按文件大小筛选:必须存在且 ≤ maxEmbeddedMediaBytes
|
|
475
|
+
for (const filePath of candidates) {
|
|
476
|
+
try {
|
|
477
|
+
if (!fs.existsSync(filePath)) continue;
|
|
478
|
+
const stat = fs.statSync(filePath);
|
|
479
|
+
if (stat.size === 0 || stat.size > maxEmbeddedMediaBytes) continue;
|
|
480
|
+
const fileName = path.basename(filePath);
|
|
481
|
+
const mimeType = guessMimeTypeFromPath(filePath);
|
|
482
|
+
const mediaType = normalizeMediaType(null, mimeType, fileName);
|
|
483
|
+
console.log(`[Task] 检测到本地文件,自动附加: ${fileName} (${Math.round(stat.size / 1024)}KB)`);
|
|
484
|
+
return {
|
|
485
|
+
media_base64: fs.readFileSync(filePath).toString('base64'),
|
|
486
|
+
media_type: mediaType,
|
|
487
|
+
media_mime_type: mimeType,
|
|
488
|
+
media_name: fileName,
|
|
489
|
+
};
|
|
490
|
+
} catch (_) {
|
|
491
|
+
// 忽略单个文件处理错误,继续检查下一个
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
return null;
|
|
496
|
+
}
|
|
497
|
+
|
|
452
498
|
function parseAgentSuccess(rawStdout) {
|
|
453
499
|
const parsed = extractLastJsonObject(rawStdout);
|
|
454
500
|
if (!parsed) {
|
|
@@ -843,6 +889,11 @@ async function handleTask(task) {
|
|
|
843
889
|
model: agentResult.raw?.result?.meta?.agentMeta?.model || null,
|
|
844
890
|
action: 'openclaw_agent_reply',
|
|
845
891
|
};
|
|
892
|
+
// 如果 agent 没有结构化附件,尝试从回复文本中提取本地文件路径
|
|
893
|
+
if (!agentResult.media && agentResult.reply) {
|
|
894
|
+
const detectedMedia = extractLocalFileFromReply(agentResult.reply);
|
|
895
|
+
if (detectedMedia) agentResult.media = detectedMedia;
|
|
896
|
+
}
|
|
846
897
|
if (agentResult.media) {
|
|
847
898
|
taskResult.mediaType = agentResult.media.media_type;
|
|
848
899
|
taskResult.mediaName = agentResult.media.media_name;
|