@aiyiran/myclaw 1.0.138 → 1.0.139
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/build-manifest.js +13 -0
- package/index.js +30 -0
- package/inject-minimax.js +1 -1
- package/inject-search.js +1 -1
- package/package.json +1 -1
- package/patch-manifest.json +15 -3
- package/skills/tavily-search/.clawhub/origin.json +7 -0
- package/skills/tavily-search/SKILL.md +92 -0
- package/skills/tavily-search/_meta.json +6 -0
- package/skills/tavily-search/scripts/search.mjs +150 -0
package/build-manifest.js
CHANGED
|
@@ -103,12 +103,25 @@ function buildManifest() {
|
|
|
103
103
|
config.patches[k] = v;
|
|
104
104
|
}
|
|
105
105
|
|
|
106
|
+
// === 4. 合并 run 命令列表(保留已有的,或使用默认的) ===
|
|
107
|
+
const DEFAULT_RUN = [
|
|
108
|
+
{
|
|
109
|
+
"module": "./inject-image",
|
|
110
|
+
"strategy": "auto",
|
|
111
|
+
"description": "图片模型注入 (vveai)"
|
|
112
|
+
}
|
|
113
|
+
];
|
|
114
|
+
const runItems = (existing.run && Array.isArray(existing.run) && existing.run.length > 0)
|
|
115
|
+
? existing.run
|
|
116
|
+
: DEFAULT_RUN;
|
|
117
|
+
|
|
106
118
|
const manifest = {
|
|
107
119
|
_doc: 'MyClaw 注入清单 (auto-generated)。strategy: auto | on | off | delete',
|
|
108
120
|
_generated: new Date().toISOString(),
|
|
109
121
|
agents,
|
|
110
122
|
skills,
|
|
111
123
|
config,
|
|
124
|
+
run: runItems,
|
|
112
125
|
};
|
|
113
126
|
|
|
114
127
|
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
package/index.js
CHANGED
|
@@ -720,6 +720,36 @@ function runPatch() {
|
|
|
720
720
|
console.log('[myclaw-config] ⚠️ 配置补丁失败: ' + err.message);
|
|
721
721
|
}
|
|
722
722
|
|
|
723
|
+
// 5. 自定义注入脚本执行引擎 (Manifest `run` 数组)
|
|
724
|
+
// 此处读取 manifest.json 中的 "run" 数组,并按顺序 require 执行对应的模块。
|
|
725
|
+
// 通过修改 manifest 即可控制额外脚本(如 inject-image, inject-search)是否跟随 patch 自动执行,
|
|
726
|
+
// 从而避免硬编码具体要注入哪些模块。
|
|
727
|
+
try {
|
|
728
|
+
const { loadManifest } = require('./patch-agent');
|
|
729
|
+
const m = loadManifest();
|
|
730
|
+
if (m && Array.isArray(m.run) && m.run.length > 0) {
|
|
731
|
+
console.log('');
|
|
732
|
+
for (const entry of m.run) {
|
|
733
|
+
const strategy = entry.strategy || 'auto';
|
|
734
|
+
const desc = entry.description || entry.module;
|
|
735
|
+
if (strategy === 'off') {
|
|
736
|
+
console.log('[myclaw-run] ⏭️ 跳过: ' + desc + ' [off]');
|
|
737
|
+
continue;
|
|
738
|
+
}
|
|
739
|
+
try {
|
|
740
|
+
console.log('[myclaw-run] ▶️ 执行: ' + desc);
|
|
741
|
+
const mod = require(entry.module);
|
|
742
|
+
mod.run([]);
|
|
743
|
+
console.log('[myclaw-run] ✅ 完成: ' + desc);
|
|
744
|
+
} catch (err) {
|
|
745
|
+
console.log('[myclaw-run] ⚠️ 失败: ' + desc + ' — ' + err.message);
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} catch (err) {
|
|
750
|
+
// manifest 加载失败,跳过
|
|
751
|
+
}
|
|
752
|
+
|
|
723
753
|
if (uiResult.success || skillResult.success || (agentResult && agentResult.success)) {
|
|
724
754
|
console.log('');
|
|
725
755
|
console.log(bar);
|
package/inject-minimax.js
CHANGED
package/inject-search.js
CHANGED
|
@@ -28,7 +28,7 @@ function run(cliArgs) {
|
|
|
28
28
|
if (!config.plugins) config.plugins = {};
|
|
29
29
|
if (!config.plugins.entries) config.plugins.entries = {};
|
|
30
30
|
|
|
31
|
-
//
|
|
31
|
+
// 备注:不支持逗号分隔的多 key 轮换
|
|
32
32
|
config.plugins.entries.tavily = {
|
|
33
33
|
enabled: true,
|
|
34
34
|
config: {
|
package/package.json
CHANGED
package/patch-manifest.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"_doc": "MyClaw 注入清单 (auto-generated)。strategy: auto | on | off | delete",
|
|
3
|
-
"_generated": "2026-04-07T16:
|
|
3
|
+
"_generated": "2026-04-07T16:26:14.875Z",
|
|
4
4
|
"agents": [
|
|
5
5
|
{
|
|
6
6
|
"id": "danci",
|
|
@@ -19,7 +19,12 @@
|
|
|
19
19
|
{
|
|
20
20
|
"name": "minimax-inject",
|
|
21
21
|
"strategy": "off",
|
|
22
|
-
"description": "minimax-inject"
|
|
22
|
+
"description": "minimax-inject (agent 可调用的注入技能)"
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"name": "tavily-search",
|
|
26
|
+
"strategy": "off",
|
|
27
|
+
"description": "tavily-search (Tavily 搜索技能包)"
|
|
23
28
|
}
|
|
24
29
|
],
|
|
25
30
|
"config": {
|
|
@@ -33,5 +38,12 @@
|
|
|
33
38
|
"plugins.entries.tavily.config.webSearch.apiKey": "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,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",
|
|
34
39
|
"update.checkOnStart": false
|
|
35
40
|
}
|
|
36
|
-
}
|
|
41
|
+
},
|
|
42
|
+
"run": [
|
|
43
|
+
{
|
|
44
|
+
"module": "./inject-image",
|
|
45
|
+
"strategy": "auto",
|
|
46
|
+
"description": "图片模型注入 (vveai)"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
37
49
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: tavily-search
|
|
3
|
+
description: Web search using Tavily's LLM-optimized API. Returns relevant results with content snippets, scores, and metadata.
|
|
4
|
+
homepage: https://tavily.com
|
|
5
|
+
metadata: {"openclaw":{"emoji":"🔍","requires":{"bins":["node"],"env":["TAVILY_API_KEY"]},"primaryEnv":"TAVILY_API_KEY"}}
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Tavily Search
|
|
9
|
+
|
|
10
|
+
Search the web and get relevant results optimized for LLM consumption.
|
|
11
|
+
|
|
12
|
+
## Authentication
|
|
13
|
+
|
|
14
|
+
Get your API key at https://tavily.com and add to your OpenClaw config:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"skills": {
|
|
19
|
+
"entries": {
|
|
20
|
+
"tavily-search": {
|
|
21
|
+
"enabled": true,
|
|
22
|
+
"apiKey": "tvly-YOUR_API_KEY_HERE"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Or set the environment variable:
|
|
30
|
+
```bash
|
|
31
|
+
export TAVILY_API_KEY="tvly-YOUR_API_KEY_HERE"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### Using the Script
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
node {baseDir}/scripts/search.mjs "query"
|
|
40
|
+
node {baseDir}/scripts/search.mjs "query" -n 10
|
|
41
|
+
node {baseDir}/scripts/search.mjs "query" --deep
|
|
42
|
+
node {baseDir}/scripts/search.mjs "query" --topic news
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Examples
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Basic search
|
|
49
|
+
node {baseDir}/scripts/search.mjs "python async patterns"
|
|
50
|
+
|
|
51
|
+
# With more results
|
|
52
|
+
node {baseDir}/scripts/search.mjs "React hooks tutorial" -n 10
|
|
53
|
+
|
|
54
|
+
# Advanced search
|
|
55
|
+
node {baseDir}/scripts/search.mjs "machine learning" --deep
|
|
56
|
+
|
|
57
|
+
# News search
|
|
58
|
+
node {baseDir}/scripts/search.mjs "AI news" --topic news
|
|
59
|
+
|
|
60
|
+
# Domain-filtered search
|
|
61
|
+
node {baseDir}/scripts/search.mjs "Python docs" --include-domains docs.python.org
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Options
|
|
65
|
+
|
|
66
|
+
| Option | Description | Default |
|
|
67
|
+
|--------|-------------|---------|
|
|
68
|
+
| `-n <count>` | Number of results (1-20) | 10 |
|
|
69
|
+
| `--depth <mode>` | Search depth: `ultra-fast`, `fast`, `basic`, `advanced` | `basic` |
|
|
70
|
+
| `--topic <topic>` | Topic: `general` or `news` | `general` |
|
|
71
|
+
| `--time-range <range>` | Time range: `day`, `week`, `month`, `year` | - |
|
|
72
|
+
| `--include-domains <domains>` | Comma-separated domains to include | - |
|
|
73
|
+
| `--exclude-domains <domains>` | Comma-separated domains to exclude | - |
|
|
74
|
+
| `--raw-content` | Include full page content | false |
|
|
75
|
+
| `--json` | Output raw JSON | false |
|
|
76
|
+
|
|
77
|
+
## Search Depth
|
|
78
|
+
|
|
79
|
+
| Depth | Latency | Relevance | Use Case |
|
|
80
|
+
|-------|---------|-----------|----------|
|
|
81
|
+
| `ultra-fast` | Lowest | Lower | Real-time chat, autocomplete |
|
|
82
|
+
| `fast` | Low | Good | Need chunks but latency matters |
|
|
83
|
+
| `basic` | Medium | High | General-purpose, balanced |
|
|
84
|
+
| `advanced` | Higher | Highest | Precision matters, research |
|
|
85
|
+
|
|
86
|
+
## Tips
|
|
87
|
+
|
|
88
|
+
- **Keep queries under 400 characters** - Think search query, not prompt
|
|
89
|
+
- **Break complex queries into sub-queries** - Better results than one massive query
|
|
90
|
+
- **Use `--include-domains`** to focus on trusted sources
|
|
91
|
+
- **Use `--time-range`** for recent information
|
|
92
|
+
- **Filter by `score`** (0-1) to get highest relevance results
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
function usage() {
|
|
4
|
+
console.error(`Usage: search.mjs "query" [options]
|
|
5
|
+
|
|
6
|
+
Options:
|
|
7
|
+
-n <count> Number of results (1-20, default: 10)
|
|
8
|
+
--depth <mode> Search depth: ultra-fast, fast, basic, advanced (default: basic)
|
|
9
|
+
--topic <topic> Topic: general or news (default: general)
|
|
10
|
+
--time-range <range> Time range: day, week, month, year
|
|
11
|
+
--include-domains <list> Comma-separated domains to include
|
|
12
|
+
--exclude-domains <list> Comma-separated domains to exclude
|
|
13
|
+
--raw-content Include full page content
|
|
14
|
+
--json Output raw JSON
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
search.mjs "python async patterns"
|
|
18
|
+
search.mjs "React hooks tutorial" -n 10
|
|
19
|
+
search.mjs "AI news" --topic news --time-range week
|
|
20
|
+
search.mjs "Python docs" --include-domains docs.python.org,realpython.com`);
|
|
21
|
+
process.exit(2);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const args = process.argv.slice(2);
|
|
25
|
+
if (args.length === 0 || args[0] === "-h" || args[0] === "--help") usage();
|
|
26
|
+
|
|
27
|
+
const query = args[0];
|
|
28
|
+
let maxResults = 10;
|
|
29
|
+
let searchDepth = "basic";
|
|
30
|
+
let topic = "general";
|
|
31
|
+
let timeRange = null;
|
|
32
|
+
let includeDomains = [];
|
|
33
|
+
let excludeDomains = [];
|
|
34
|
+
let includeRawContent = false;
|
|
35
|
+
let outputJson = false;
|
|
36
|
+
|
|
37
|
+
for (let i = 1; i < args.length; i++) {
|
|
38
|
+
const a = args[i];
|
|
39
|
+
if (a === "-n") {
|
|
40
|
+
maxResults = Number.parseInt(args[i + 1] ?? "10", 10);
|
|
41
|
+
i++;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
if (a === "--depth") {
|
|
45
|
+
searchDepth = args[i + 1] ?? "basic";
|
|
46
|
+
i++;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (a === "--topic") {
|
|
50
|
+
topic = args[i + 1] ?? "general";
|
|
51
|
+
i++;
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (a === "--time-range") {
|
|
55
|
+
timeRange = args[i + 1];
|
|
56
|
+
i++;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (a === "--include-domains") {
|
|
60
|
+
includeDomains = (args[i + 1] ?? "").split(",").map(d => d.trim()).filter(Boolean);
|
|
61
|
+
i++;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (a === "--exclude-domains") {
|
|
65
|
+
excludeDomains = (args[i + 1] ?? "").split(",").map(d => d.trim()).filter(Boolean);
|
|
66
|
+
i++;
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (a === "--raw-content") {
|
|
70
|
+
includeRawContent = true;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
if (a === "--json") {
|
|
74
|
+
outputJson = true;
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
console.error(`Unknown arg: ${a}`);
|
|
78
|
+
usage();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const apiKey = (process.env.TAVILY_API_KEY ?? "").trim();
|
|
82
|
+
if (!apiKey) {
|
|
83
|
+
console.error("Error: TAVILY_API_KEY not set");
|
|
84
|
+
console.error("Get your API key at https://tavily.com");
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const body = {
|
|
89
|
+
query: query,
|
|
90
|
+
max_results: Math.max(1, Math.min(maxResults, 20)),
|
|
91
|
+
search_depth: searchDepth,
|
|
92
|
+
topic: topic,
|
|
93
|
+
include_raw_content: includeRawContent,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
if (timeRange) body.time_range = timeRange;
|
|
97
|
+
if (includeDomains.length > 0) body.include_domains = includeDomains;
|
|
98
|
+
if (excludeDomains.length > 0) body.exclude_domains = excludeDomains;
|
|
99
|
+
|
|
100
|
+
const resp = await fetch("https://api.tavily.com/search", {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/json",
|
|
104
|
+
"Authorization": `Bearer ${apiKey}`,
|
|
105
|
+
},
|
|
106
|
+
body: JSON.stringify(body),
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (!resp.ok) {
|
|
110
|
+
const text = await resp.text().catch(() => "");
|
|
111
|
+
throw new Error(`Tavily Search failed (${resp.status}): ${text}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const data = await resp.json();
|
|
115
|
+
|
|
116
|
+
if (outputJson) {
|
|
117
|
+
console.log(JSON.stringify(data, null, 2));
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Print AI answer if available
|
|
122
|
+
if (data.answer) {
|
|
123
|
+
console.log("## Answer\n");
|
|
124
|
+
console.log(data.answer);
|
|
125
|
+
console.log("\n---\n");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Print results
|
|
129
|
+
const results = (data.results ?? []).slice(0, maxResults);
|
|
130
|
+
console.log(`## Sources (${results.length} results)\n`);
|
|
131
|
+
|
|
132
|
+
for (const r of results) {
|
|
133
|
+
const title = String(r?.title ?? "").trim();
|
|
134
|
+
const url = String(r?.url ?? "").trim();
|
|
135
|
+
const content = String(r?.content ?? "").trim();
|
|
136
|
+
const score = r?.score ? ` (relevance: ${(r.score * 100).toFixed(0)}%)` : "";
|
|
137
|
+
|
|
138
|
+
if (!title || !url) continue;
|
|
139
|
+
|
|
140
|
+
console.log(`- **${title}**${score}`);
|
|
141
|
+
console.log(` ${url}`);
|
|
142
|
+
if (content) {
|
|
143
|
+
console.log(` ${content.slice(0, 300)}${content.length > 300 ? "..." : ""}`);
|
|
144
|
+
}
|
|
145
|
+
console.log();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (data.response_time) {
|
|
149
|
+
console.log(`\nResponse time: ${data.response_time}s`);
|
|
150
|
+
}
|