openclaw-multi-auto 1.4.5 → 1.4.7
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/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/dist/plugin-sdk/mattermost.js +3 -3
- package/dist/plugin-sdk/signal.js +2 -2
- package/docs/browser-architecture.md +602 -0
- package/extensions/googlechat/node_modules/.bin/openclaw +2 -2
- package/extensions/memory-core/node_modules/.bin/openclaw +2 -2
- package/extensions/memory-lancedb/node_modules/.bin/openai +2 -2
- package/extensions/page-action-cache/dist/actions-executor.d.ts +62 -0
- package/extensions/page-action-cache/dist/actions-executor.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/actions-executor.js +339 -0
- package/extensions/page-action-cache/dist/actions-executor.js.map +1 -0
- package/extensions/page-action-cache/dist/cache-invalidator.d.ts +70 -0
- package/extensions/page-action-cache/dist/cache-invalidator.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/cache-invalidator.js +212 -0
- package/extensions/page-action-cache/dist/cache-invalidator.js.map +1 -0
- package/extensions/page-action-cache/dist/cache-store.d.ts +80 -0
- package/extensions/page-action-cache/dist/cache-store.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/cache-store.js +361 -0
- package/extensions/page-action-cache/dist/cache-store.js.map +1 -0
- package/extensions/page-action-cache/dist/cache-strategy.d.ts +65 -0
- package/extensions/page-action-cache/dist/cache-strategy.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/cache-strategy.js +237 -0
- package/extensions/page-action-cache/dist/cache-strategy.js.map +1 -0
- package/extensions/page-action-cache/dist/hooks-entry.d.ts +18 -0
- package/extensions/page-action-cache/dist/hooks-entry.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/hooks-entry.js +27 -0
- package/extensions/page-action-cache/dist/hooks-entry.js.map +1 -0
- package/extensions/page-action-cache/dist/hooks.d.ts +10 -0
- package/extensions/page-action-cache/dist/hooks.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/hooks.js +277 -0
- package/extensions/page-action-cache/dist/hooks.js.map +1 -0
- package/extensions/page-action-cache/dist/index.d.ts +24 -0
- package/extensions/page-action-cache/dist/index.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/index.js +34 -0
- package/extensions/page-action-cache/dist/index.js.map +1 -0
- package/extensions/page-action-cache/dist/scenario-recognizer.d.ts +45 -0
- package/extensions/page-action-cache/dist/scenario-recognizer.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/scenario-recognizer.js +213 -0
- package/extensions/page-action-cache/dist/scenario-recognizer.js.map +1 -0
- package/extensions/page-action-cache/dist/security-policy.d.ts +62 -0
- package/extensions/page-action-cache/dist/security-policy.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/security-policy.js +219 -0
- package/extensions/page-action-cache/dist/security-policy.js.map +1 -0
- package/extensions/page-action-cache/dist/tools.d.ts +209 -0
- package/extensions/page-action-cache/dist/tools.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/tools.js +383 -0
- package/extensions/page-action-cache/dist/tools.js.map +1 -0
- package/extensions/page-action-cache/dist/types.d.ts +336 -0
- package/extensions/page-action-cache/dist/types.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/types.js +8 -0
- package/extensions/page-action-cache/dist/types.js.map +1 -0
- package/extensions/page-action-cache/dist/ux-enhancer.d.ts +60 -0
- package/extensions/page-action-cache/dist/ux-enhancer.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/ux-enhancer.js +218 -0
- package/extensions/page-action-cache/dist/ux-enhancer.js.map +1 -0
- package/extensions/page-action-cache/dist/variable-resolver.d.ts +28 -0
- package/extensions/page-action-cache/dist/variable-resolver.d.ts.map +1 -0
- package/extensions/page-action-cache/dist/variable-resolver.js +201 -0
- package/extensions/page-action-cache/dist/variable-resolver.js.map +1 -0
- package/extensions/page-action-cache/docs/API.md +555 -0
- package/extensions/page-action-cache/docs/IMPLEMENTATION.md +1792 -0
- package/extensions/page-action-cache/docs/INTEGRATION.md +387 -0
- package/extensions/page-action-cache/docs/README.md +183 -0
- package/extensions/page-action-cache/index.ts +118 -0
- package/extensions/page-action-cache/node_modules/.bin/nlc +21 -0
- package/extensions/page-action-cache/node_modules/.bin/node-llama-cpp +21 -0
- package/extensions/page-action-cache/node_modules/.bin/openclaw +21 -0
- package/extensions/page-action-cache/node_modules/.bin/tsc +21 -0
- package/extensions/page-action-cache/node_modules/.bin/tsserver +21 -0
- package/extensions/page-action-cache/node_modules/.bin/vitest +21 -0
- package/extensions/page-action-cache/openclaw.plugin.json +208 -0
- package/extensions/page-action-cache/package.json +76 -0
- package/extensions/page-action-cache/scripts/npm_publish.sh +80 -0
- package/extensions/page-action-cache/skills/page-action-cache/SKILL.md +216 -0
- package/extensions/page-action-cache/src/actions-executor.ts +441 -0
- package/extensions/page-action-cache/src/cache-invalidator.ts +271 -0
- package/extensions/page-action-cache/src/cache-store.ts +457 -0
- package/extensions/page-action-cache/src/cache-strategy.ts +327 -0
- package/extensions/page-action-cache/src/hooks-entry.ts +114 -0
- package/extensions/page-action-cache/src/hooks.ts +332 -0
- package/extensions/page-action-cache/src/index.ts +104 -0
- package/extensions/page-action-cache/src/scenario-recognizer.ts +259 -0
- package/extensions/page-action-cache/src/security-policy.ts +268 -0
- package/extensions/page-action-cache/src/tools.ts +437 -0
- package/extensions/page-action-cache/src/types.ts +482 -0
- package/extensions/page-action-cache/src/ux-enhancer.ts +266 -0
- package/extensions/page-action-cache/src/variable-resolver.ts +258 -0
- package/extensions/page-action-cache/tests/actions-executor.test.ts +424 -0
- package/extensions/page-action-cache/tests/cache-store.test.ts +267 -0
- package/extensions/page-action-cache/tests/integration-test.ts +62 -0
- package/extensions/page-action-cache/tests/scenario-recognizer.test.ts +140 -0
- package/extensions/page-action-cache/tests/variable-resolver.test.ts +187 -0
- package/extensions/page-action-cache/tsconfig.json +39 -0
- package/package.json +1 -1
- package/scripts/create-instance.sh +26 -8
- package/scripts/npm_publish.sh +59 -1
- package/scripts/publish-extension.sh +343 -0
- package/ui/node_modules/.bin/vite +2 -2
- package/ui/node_modules/.bin/vitest +2 -2
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hooks Integration
|
|
3
|
+
* Hooks 集成 - 注册 OpenClaw Hooks 来拦截和增强浏览器操作
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { CacheConfig, PageViewport } from "./types.js";
|
|
7
|
+
import { getCacheStore } from "./cache-store.js";
|
|
8
|
+
import { getScenarioRecognizer } from "./scenario-recognizer.js";
|
|
9
|
+
import { getVariableResolver } from "./variable-resolver.js";
|
|
10
|
+
|
|
11
|
+
// ============================================================================
|
|
12
|
+
// 默认配置
|
|
13
|
+
// ============================================================================
|
|
14
|
+
|
|
15
|
+
const DEFAULT_CONFIG: CacheConfig = {
|
|
16
|
+
// === 全局开关 ===
|
|
17
|
+
enabled: true,
|
|
18
|
+
autoUseCache: true,
|
|
19
|
+
|
|
20
|
+
// === 场景识别 ===
|
|
21
|
+
scenarioRecognitionEnabled: true,
|
|
22
|
+
llmClassificationThreshold: 70,
|
|
23
|
+
|
|
24
|
+
// === 缓存策略 ===
|
|
25
|
+
cacheLevelStrategy: "auto",
|
|
26
|
+
defaultCacheLevel: "L3",
|
|
27
|
+
|
|
28
|
+
// === 缓存失效 ===
|
|
29
|
+
pageChangeDetectionEnabled: true,
|
|
30
|
+
changeInvalidationThreshold: 80,
|
|
31
|
+
invalidationStrategy: "soft",
|
|
32
|
+
maxVersionsPerEntry: 3,
|
|
33
|
+
|
|
34
|
+
// === 变量系统 ===
|
|
35
|
+
variableExtractionEnabled: true,
|
|
36
|
+
allowUserConfirmVariables: false,
|
|
37
|
+
|
|
38
|
+
// === 安全 ===
|
|
39
|
+
encryptSensitiveCache: false,
|
|
40
|
+
accessControlEnabled: false,
|
|
41
|
+
allowedUserIds: [],
|
|
42
|
+
logSanitizationEnabled: true,
|
|
43
|
+
|
|
44
|
+
// === 用户体验 ===
|
|
45
|
+
showCacheStatusToUser: true,
|
|
46
|
+
enableUserCacheConfirmation: false,
|
|
47
|
+
enableUserForcedRefresh: true,
|
|
48
|
+
enableUserCacheErrorReport: true,
|
|
49
|
+
|
|
50
|
+
// === 统计 ===
|
|
51
|
+
trackExecutionStats: true,
|
|
52
|
+
statsUpdateInterval: 60,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// ============================================================================
|
|
56
|
+
// Hooks 注册
|
|
57
|
+
// ============================================================================
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 注册页面操作缓存扩展的 Hooks
|
|
61
|
+
*/
|
|
62
|
+
export function registerPageActionCacheHooks(api: any, config?: Partial<CacheConfig>): void {
|
|
63
|
+
const finalConfig = { ...DEFAULT_CONFIG, ...config };
|
|
64
|
+
|
|
65
|
+
if (!finalConfig.enabled) {
|
|
66
|
+
console.log("[PageActionCache] Extension disabled");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const cacheStore = getCacheStore();
|
|
71
|
+
const scenarioRecognizer = getScenarioRecognizer();
|
|
72
|
+
const variableResolver = getVariableResolver();
|
|
73
|
+
// UX enhancer for future use
|
|
74
|
+
// const uxEnhancer = getUXEnhancer();
|
|
75
|
+
|
|
76
|
+
// -------------------------------------------------------------------------
|
|
77
|
+
// before_tool_call hook - 场景识别和缓存拦截
|
|
78
|
+
// -------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
api.registerHook({
|
|
81
|
+
name: "before_tool_call",
|
|
82
|
+
priority: 100,
|
|
83
|
+
description: "页面操作缓存 - 场景识别和缓存拦截",
|
|
84
|
+
|
|
85
|
+
async handler(event: any, ctx: any) {
|
|
86
|
+
if (event.tool.name !== "browser") {
|
|
87
|
+
return { block: false };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const params = event.params;
|
|
91
|
+
const url = params.url as string;
|
|
92
|
+
const action = params.action as string;
|
|
93
|
+
|
|
94
|
+
// 只缓存特定操作
|
|
95
|
+
const cacheActions = ["navigate", "click", "type", "press", "hover"];
|
|
96
|
+
if (!cacheActions.includes(action)) {
|
|
97
|
+
return { block: false };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 提取用户输入
|
|
101
|
+
const userInput = extractUserInput(ctx);
|
|
102
|
+
const currentUrl = url || ctx.currentUrl || "";
|
|
103
|
+
|
|
104
|
+
// 场景识别
|
|
105
|
+
if (finalConfig.scenarioRecognitionEnabled) {
|
|
106
|
+
const match = await scenarioRecognizer.recognize(userInput, currentUrl);
|
|
107
|
+
|
|
108
|
+
if (match && match.confidence >= 80) {
|
|
109
|
+
// 高置信度匹配,返回 cacheInfo
|
|
110
|
+
return {
|
|
111
|
+
block: false,
|
|
112
|
+
result: {
|
|
113
|
+
cacheInfo: {
|
|
114
|
+
exists: true,
|
|
115
|
+
scenario: match.scenario,
|
|
116
|
+
confidence: match.confidence,
|
|
117
|
+
method: match.method,
|
|
118
|
+
matchedPattern: match.matchedPattern,
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 检查缓存
|
|
126
|
+
const viewport = extractViewport(ctx, params);
|
|
127
|
+
const entry = cacheStore.get(url, viewport);
|
|
128
|
+
|
|
129
|
+
if (entry) {
|
|
130
|
+
const stats = cacheStore.getStats();
|
|
131
|
+
// L3 缓存命中,返回 cacheInfo
|
|
132
|
+
return {
|
|
133
|
+
block: false,
|
|
134
|
+
result: {
|
|
135
|
+
cacheInfo: {
|
|
136
|
+
exists: true,
|
|
137
|
+
cacheLevel: entry.cacheLevel,
|
|
138
|
+
scenario: entry.scenario,
|
|
139
|
+
actions: entry.actions,
|
|
140
|
+
variables: entry.variables,
|
|
141
|
+
cacheKey: entry.key,
|
|
142
|
+
},
|
|
143
|
+
stats: {
|
|
144
|
+
hitRate: stats.hitRate,
|
|
145
|
+
savedTokens: stats.savedTokens,
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return { block: false };
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
// -------------------------------------------------------------------------
|
|
156
|
+
// after_tool_call hook - 保存 LLM 分析结果到缓存
|
|
157
|
+
// -------------------------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
api.registerHook({
|
|
160
|
+
name: "after_tool_call",
|
|
161
|
+
priority: 100,
|
|
162
|
+
description: "页面操作缓存 - 保存 LLM 分析结果",
|
|
163
|
+
|
|
164
|
+
async handler(event: any, ctx: any) {
|
|
165
|
+
if (event.tool.name !== "browser") {
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const params = event.params;
|
|
170
|
+
const url = params.url as string;
|
|
171
|
+
const action = params.action as string;
|
|
172
|
+
|
|
173
|
+
// 只缓存特定操作
|
|
174
|
+
const cacheActions = ["navigate", "click", "type", "press", "hover"];
|
|
175
|
+
if (!cacheActions.includes(action)) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// 检查 LLM 是否返回了场景类型
|
|
180
|
+
const result = event.result;
|
|
181
|
+
const llmScenario = result.scenario as string | undefined;
|
|
182
|
+
|
|
183
|
+
if (!llmScenario) {
|
|
184
|
+
// 没有 LLM 标记的场景,不保存
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 识别场景并获取变量
|
|
189
|
+
const userInput = extractUserInput(ctx);
|
|
190
|
+
const scenarioMatch = await scenarioRecognizer.recognize(userInput, url);
|
|
191
|
+
|
|
192
|
+
// 决定缓存层级
|
|
193
|
+
let cacheLevel: "L3" | "L2" | "L1" = "L3";
|
|
194
|
+
if (finalConfig.cacheLevelStrategy === "auto" && scenarioMatch) {
|
|
195
|
+
cacheLevel =
|
|
196
|
+
scenarioMatch.confidence >= 85
|
|
197
|
+
? "L3"
|
|
198
|
+
: scenarioMatch.confidence >= 70
|
|
199
|
+
? "L2"
|
|
200
|
+
: "L1";
|
|
201
|
+
} else {
|
|
202
|
+
cacheLevel = finalConfig.defaultCacheLevel as "L3" | "L2" | "L1";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// 解析变量
|
|
206
|
+
let variables: Record<string, string> | undefined;
|
|
207
|
+
if (finalConfig.variableExtractionEnabled && scenarioMatch) {
|
|
208
|
+
variables = variableResolver.resolveVariables(
|
|
209
|
+
userInput,
|
|
210
|
+
scenarioMatch.scenario
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// 保存到缓存
|
|
215
|
+
const viewport = extractViewport(ctx, params);
|
|
216
|
+
const actions = result.actions || [];
|
|
217
|
+
|
|
218
|
+
if (actions.length > 0) {
|
|
219
|
+
cacheStore.set(url, viewport, actions, {
|
|
220
|
+
scenario: scenarioMatch?.scenario || llmScenario,
|
|
221
|
+
cacheLevel,
|
|
222
|
+
variables,
|
|
223
|
+
description: result.description || `Cached ${llmScenario} actions`,
|
|
224
|
+
pageType: result.pageType || "mixed",
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
console.log(
|
|
228
|
+
`[PageActionCache] Saved cache entry: ${scenarioMatch?.scenario || llmScenario} (${cacheLevel})`
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
// -------------------------------------------------------------------------
|
|
235
|
+
// before_prompt_build hook - 注入缓存使用指南
|
|
236
|
+
// -------------------------------------------------------------------------
|
|
237
|
+
|
|
238
|
+
api.registerHook({
|
|
239
|
+
name: "before_prompt_build",
|
|
240
|
+
priority: 50,
|
|
241
|
+
description: "页面操作缓存 - 注入缓存使用指南",
|
|
242
|
+
|
|
243
|
+
async handler(_event: any, _ctx: any) {
|
|
244
|
+
if (!finalConfig.autoUseCache) {
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const stats = cacheStore.getStats();
|
|
249
|
+
|
|
250
|
+
// 只在命中率较高时注入
|
|
251
|
+
if (stats.hitRate < 30) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// 构建缓存使用指南
|
|
256
|
+
const guide = `
|
|
257
|
+
【页面操作缓存系统】
|
|
258
|
+
|
|
259
|
+
当前缓存统计:
|
|
260
|
+
- 总条目数:${stats.totalEntries}
|
|
261
|
+
- 总命中:${stats.totalHits}
|
|
262
|
+
- 命中率:${stats.hitRate.toFixed(1)}%
|
|
263
|
+
|
|
264
|
+
已缓存的场景:
|
|
265
|
+
${cacheStore.getTopScenarios(5)}
|
|
266
|
+
|
|
267
|
+
使用方式:
|
|
268
|
+
1. 如果工具返回 cacheInfo 信息,优先使用 execute_cached 工具
|
|
269
|
+
2. 执行缓存操作可以大幅降低 token 消耗和操作延迟
|
|
270
|
+
3. 如果页面结构变化,缓存会自动失效
|
|
271
|
+
|
|
272
|
+
支持的场景:login, logout, search, checkout, settings, form_fill, navigate, screenshot
|
|
273
|
+
|
|
274
|
+
缓存层级:
|
|
275
|
+
- L3: 场景级缓存(包含变量模板)- 高置信度时直接使用
|
|
276
|
+
- L2: 流程级缓存 - 中置信度时使用
|
|
277
|
+
- L1: 原子级缓存 - 低置信度或无场景时使用
|
|
278
|
+
|
|
279
|
+
可用的缓存工具:
|
|
280
|
+
- execute_cached: 执行缓存的操作序列
|
|
281
|
+
- cache_stats: 查看缓存统计
|
|
282
|
+
- cache_list: 列出所有缓存
|
|
283
|
+
- cache_clear: 清空缓存
|
|
284
|
+
- scenario_list: 列出所有场景
|
|
285
|
+
- force_refresh: 强制刷新指定缓存
|
|
286
|
+
`;
|
|
287
|
+
|
|
288
|
+
return { prependContext: guide };
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
console.log("[PageActionCache] Hooks registered successfully");
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// ============================================================================
|
|
296
|
+
// 辅助函数
|
|
297
|
+
// ============================================================================
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* 从上下文提取用户输入
|
|
301
|
+
*/
|
|
302
|
+
function extractUserInput(ctx: any): string {
|
|
303
|
+
// 尝试从上下文中提取用户输入
|
|
304
|
+
if (ctx.message?.content) {
|
|
305
|
+
return ctx.message.content;
|
|
306
|
+
}
|
|
307
|
+
if (ctx.userInput) {
|
|
308
|
+
return ctx.userInput;
|
|
309
|
+
}
|
|
310
|
+
if (ctx.prompt) {
|
|
311
|
+
return ctx.prompt;
|
|
312
|
+
}
|
|
313
|
+
return "";
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* 从上下文和参数提取视口尺寸
|
|
318
|
+
*/
|
|
319
|
+
function extractViewport(ctx: any, params: any): PageViewport {
|
|
320
|
+
// 优先从参数获取
|
|
321
|
+
if (params.viewport) {
|
|
322
|
+
return params.viewport;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// 其次从上下文获取
|
|
326
|
+
if (ctx.viewport) {
|
|
327
|
+
return ctx.viewport;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// 默认值
|
|
331
|
+
return { width: 1920, height: 1080 };
|
|
332
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Page Action Cache Extension - Main Exports
|
|
3
|
+
* 页面操作缓存扩展 - 主要导出
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// 核心模块导出
|
|
8
|
+
// ============================================================================
|
|
9
|
+
|
|
10
|
+
export { getCacheStore } from "./cache-store.js";
|
|
11
|
+
export { getScenarioRecognizer } from "./scenario-recognizer.js";
|
|
12
|
+
export { getVariableResolver } from "./variable-resolver.js";
|
|
13
|
+
export { getActionsExecutor } from "./actions-executor.js";
|
|
14
|
+
export { getSecurityPolicy } from "./security-policy.js";
|
|
15
|
+
export { getCacheInvalidator } from "./cache-invalidator.js";
|
|
16
|
+
export { getUXEnhancer } from "./ux-enhancer.js";
|
|
17
|
+
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// 类型导出
|
|
20
|
+
// ============================================================================
|
|
21
|
+
|
|
22
|
+
export type {
|
|
23
|
+
AtomicAction,
|
|
24
|
+
Action,
|
|
25
|
+
AtomicActionType,
|
|
26
|
+
NavigateAction,
|
|
27
|
+
ClickAction,
|
|
28
|
+
TypeAction,
|
|
29
|
+
PressAction,
|
|
30
|
+
HoverAction,
|
|
31
|
+
ScreenshotAction,
|
|
32
|
+
EvaluateAction,
|
|
33
|
+
CompositeAction,
|
|
34
|
+
PageViewport,
|
|
35
|
+
VariableMap,
|
|
36
|
+
CacheLevel,
|
|
37
|
+
PageType,
|
|
38
|
+
CacheSource,
|
|
39
|
+
DOMHashFeatures,
|
|
40
|
+
PageChangeDetection,
|
|
41
|
+
PageActionCacheEntry,
|
|
42
|
+
ScenarioRule,
|
|
43
|
+
ScenarioMatch,
|
|
44
|
+
CacheStats,
|
|
45
|
+
CacheConfig,
|
|
46
|
+
ExecutionResult,
|
|
47
|
+
PwAi,
|
|
48
|
+
} from "./types.js";
|
|
49
|
+
|
|
50
|
+
// ============================================================================
|
|
51
|
+
// Hooks 导出
|
|
52
|
+
// ============================================================================
|
|
53
|
+
|
|
54
|
+
export { registerPageActionCacheHooks } from "./hooks.js";
|
|
55
|
+
export { initializePageActionCache, pageActionCacheMeta } from "./hooks-entry.js";
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Tools 导出
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
export {
|
|
62
|
+
createExecuteCachedTool,
|
|
63
|
+
executeCached,
|
|
64
|
+
} from "./tools.js";
|
|
65
|
+
|
|
66
|
+
export {
|
|
67
|
+
createCacheListTool,
|
|
68
|
+
cacheList,
|
|
69
|
+
} from "./tools.js";
|
|
70
|
+
|
|
71
|
+
export {
|
|
72
|
+
createCacheStatsTool,
|
|
73
|
+
cacheStats,
|
|
74
|
+
} from "./tools.js";
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
createCacheClearTool,
|
|
78
|
+
cacheClear,
|
|
79
|
+
} from "./tools.js";
|
|
80
|
+
|
|
81
|
+
export {
|
|
82
|
+
createScenarioListTool,
|
|
83
|
+
scenarioList,
|
|
84
|
+
} from "./tools.js";
|
|
85
|
+
|
|
86
|
+
export {
|
|
87
|
+
createForceRefreshTool,
|
|
88
|
+
forceRefresh,
|
|
89
|
+
} from "./tools.js";
|
|
90
|
+
|
|
91
|
+
export { CACHE_TOOLS } from "./tools.js";
|
|
92
|
+
|
|
93
|
+
// ============================================================================
|
|
94
|
+
// Cache Strategy 导出
|
|
95
|
+
// ============================================================================
|
|
96
|
+
|
|
97
|
+
export {
|
|
98
|
+
AutoCacheStrategy,
|
|
99
|
+
L3OnlyCacheStrategy,
|
|
100
|
+
L2OnlyCacheStrategy,
|
|
101
|
+
L1OnlyCacheStrategy,
|
|
102
|
+
} from "./cache-strategy.js";
|
|
103
|
+
|
|
104
|
+
export type { CacheStrategy } from "./cache-strategy.js";
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scenario Recognizer
|
|
3
|
+
* 场景识别器 - 多层匹配策略
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
ScenarioRule,
|
|
8
|
+
ScenarioMatch,
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
import { getCacheStore } from "./cache-store.js";
|
|
11
|
+
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// 精确场景规则
|
|
14
|
+
// ============================================================================
|
|
15
|
+
|
|
16
|
+
const EXACT_SCENARIO_RULES: ScenarioRule[] = [
|
|
17
|
+
{
|
|
18
|
+
scenario: "login",
|
|
19
|
+
keywords: ["登录", "登陆", "注册", "进入", "login to", "sign in", "账号"],
|
|
20
|
+
urlPatterns: ["/login", "/signin", "/auth", "/account"],
|
|
21
|
+
priority: 100,
|
|
22
|
+
cacheLevel: "L3",
|
|
23
|
+
confidence: 90,
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
scenario: "logout",
|
|
27
|
+
keywords: ["退出", "登出", "注销", "sign out", "logout"],
|
|
28
|
+
urlPatterns: ["/logout", "/signout", "/signout"],
|
|
29
|
+
priority: 100,
|
|
30
|
+
cacheLevel: "L3",
|
|
31
|
+
confidence: 90,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
scenario: "search",
|
|
35
|
+
keywords: ["搜索", "查找", "找", "search for", "查询"],
|
|
36
|
+
urlPatterns: ["/search", "/query", "/list"],
|
|
37
|
+
priority: 80,
|
|
38
|
+
cacheLevel: "L3",
|
|
39
|
+
confidence: 80,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
scenario: "checkout",
|
|
43
|
+
keywords: ["结账", "结算", "支付", "收银", "买单", "checkout"],
|
|
44
|
+
urlPatterns: ["/checkout", "/cart", "/payment"],
|
|
45
|
+
priority: 90,
|
|
46
|
+
cacheLevel: "L3",
|
|
47
|
+
confidence: 85,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
scenario: "settings",
|
|
51
|
+
keywords: ["设置", "配置", "修改", "编辑", "个人中心"],
|
|
52
|
+
urlPatterns: ["/settings", "/config", "/profile"],
|
|
53
|
+
priority: 75,
|
|
54
|
+
cacheLevel: "L3",
|
|
55
|
+
confidence: 75,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
scenario: "form_fill",
|
|
59
|
+
keywords: ["填写", "填表单", "提交", "submit form"],
|
|
60
|
+
urlPatterns: [], // 不依赖 URL
|
|
61
|
+
priority: 70,
|
|
62
|
+
cacheLevel: "L2",
|
|
63
|
+
confidence: 70,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
scenario: "navigate",
|
|
67
|
+
keywords: ["打开", "进入", "跳转", "go to", "open", "navigate"],
|
|
68
|
+
urlPatterns: [],
|
|
69
|
+
priority: 60,
|
|
70
|
+
cacheLevel: "L2",
|
|
71
|
+
confidence: 60,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
scenario: "screenshot",
|
|
75
|
+
keywords: ["截图", "截图", "snapshot", "screenshot"],
|
|
76
|
+
urlPatterns: [],
|
|
77
|
+
priority: 50,
|
|
78
|
+
cacheLevel: "L1",
|
|
79
|
+
confidence: 50,
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Scenario Recognizer 类
|
|
85
|
+
// ============================================================================
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* 场景识别器
|
|
89
|
+
*/
|
|
90
|
+
export class ScenarioRecognizer {
|
|
91
|
+
private cacheStore = getCacheStore();
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 多层匹配策略
|
|
95
|
+
*/
|
|
96
|
+
async recognize(
|
|
97
|
+
userInput: string,
|
|
98
|
+
currentUrl: string
|
|
99
|
+
): Promise<ScenarioMatch | null> {
|
|
100
|
+
// 1. 精确规则匹配(高优先级,低误判)
|
|
101
|
+
const exactMatch = this.matchExactRules(userInput, currentUrl);
|
|
102
|
+
if (exactMatch && exactMatch.confidence >= 90) {
|
|
103
|
+
return exactMatch;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// 2. 语义理解(使用 LLM)
|
|
107
|
+
const llmMatch = await this.matchWithLLM(userInput, currentUrl);
|
|
108
|
+
if (llmMatch && llmMatch.confidence >= 70) {
|
|
109
|
+
return llmMatch;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 3. 历史学习(相似输入匹配)
|
|
113
|
+
const historyMatch = this.matchHistory(userInput);
|
|
114
|
+
if (historyMatch && historyMatch.confidence >= 60) {
|
|
115
|
+
return historyMatch;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return null; // 都不匹配,交给 LLM 分析
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 精确规则匹配
|
|
123
|
+
*/
|
|
124
|
+
private matchExactRules(
|
|
125
|
+
userInput: string,
|
|
126
|
+
url: string
|
|
127
|
+
): ScenarioMatch | null {
|
|
128
|
+
const normalizedInput = userInput.toLowerCase();
|
|
129
|
+
const normalizedUrl = url.toLowerCase();
|
|
130
|
+
|
|
131
|
+
for (const rule of EXACT_SCENARIO_RULES) {
|
|
132
|
+
// 关键词匹配
|
|
133
|
+
for (const keyword of rule.keywords) {
|
|
134
|
+
if (normalizedInput.includes(keyword.toLowerCase())) {
|
|
135
|
+
return {
|
|
136
|
+
scenario: rule.scenario,
|
|
137
|
+
confidence: rule.priority as number,
|
|
138
|
+
method: "keyword",
|
|
139
|
+
matchedPattern: keyword,
|
|
140
|
+
} as ScenarioMatch;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// URL 模式匹配
|
|
145
|
+
for (const pattern of rule.urlPatterns) {
|
|
146
|
+
if (normalizedUrl.includes(pattern.toLowerCase())) {
|
|
147
|
+
return {
|
|
148
|
+
scenario: rule.scenario,
|
|
149
|
+
confidence: rule.priority - 10, // 略低于纯关键词
|
|
150
|
+
method: "url",
|
|
151
|
+
matchedPattern: pattern,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* LLM 分类
|
|
162
|
+
*/
|
|
163
|
+
private async matchWithLLM(
|
|
164
|
+
_userInput: string,
|
|
165
|
+
_url: string
|
|
166
|
+
): Promise<ScenarioMatch | null> {
|
|
167
|
+
// TODO: 实现 LLM 分类
|
|
168
|
+
// 使用 OpenClaw 的 LLM 进行意图分类
|
|
169
|
+
// 返回场景类型和置信度
|
|
170
|
+
|
|
171
|
+
// 暂时返回 null,等待 LLM 集成
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 历史匹配
|
|
177
|
+
*/
|
|
178
|
+
private matchHistory(userInput: string): ScenarioMatch | null {
|
|
179
|
+
const stats = this.cacheStore.getStats();
|
|
180
|
+
|
|
181
|
+
// 如果命中率太低,不做历史匹配
|
|
182
|
+
if (stats.hitRate < 30) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// 在历史记录中查找相似输入
|
|
187
|
+
// 计算 Jaccard 相似度
|
|
188
|
+
const scenarioMatch = this.cacheStore.getScenarioMatch("search"); // 示例
|
|
189
|
+
|
|
190
|
+
if (scenarioMatch) {
|
|
191
|
+
const similarity = this.calculateJaccardSimilarity(
|
|
192
|
+
userInput,
|
|
193
|
+
scenarioMatch.scenario
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
if (similarity >= 0.6) {
|
|
197
|
+
return {
|
|
198
|
+
scenario: scenarioMatch.scenario,
|
|
199
|
+
confidence: Math.round(similarity * 100),
|
|
200
|
+
method: "history",
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* 计算 Jaccard 相似度
|
|
210
|
+
*/
|
|
211
|
+
private calculateJaccardSimilarity(str1: string, str2: string): number {
|
|
212
|
+
const set1 = new Set(str1.toLowerCase().split(/\s+/));
|
|
213
|
+
const set2 = new Set(str2.toLowerCase().split(/\s+/));
|
|
214
|
+
|
|
215
|
+
const intersection = new Set([...set1].filter((x) => set2.has(x)));
|
|
216
|
+
const union = new Set([...set1, ...set2]);
|
|
217
|
+
|
|
218
|
+
return intersection.size / union.size;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// -------------------------------------------------------------------------
|
|
222
|
+
// 辅助方法
|
|
223
|
+
// -------------------------------------------------------------------------
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* 获取所有规则
|
|
227
|
+
*/
|
|
228
|
+
getRules(): ScenarioRule[] {
|
|
229
|
+
return [...EXACT_SCENARIO_RULES];
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* 添加自定义规则
|
|
234
|
+
*/
|
|
235
|
+
addRule(rule: ScenarioRule): void {
|
|
236
|
+
EXACT_SCENARIO_RULES.push(rule);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* 获取建议的缓存层级
|
|
241
|
+
*/
|
|
242
|
+
getRecommendedCacheLevel(scenario: string): "L3" | "L2" | "L1" {
|
|
243
|
+
const rule = EXACT_SCENARIO_RULES.find((r) => r.scenario === scenario);
|
|
244
|
+
return rule?.cacheLevel || "L1";
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// ============================================================================
|
|
249
|
+
// 单例
|
|
250
|
+
// ============================================================================
|
|
251
|
+
|
|
252
|
+
let scenarioRecognizerInstance: ScenarioRecognizer | null = null;
|
|
253
|
+
|
|
254
|
+
export function getScenarioRecognizer(): ScenarioRecognizer {
|
|
255
|
+
if (!scenarioRecognizerInstance) {
|
|
256
|
+
scenarioRecognizerInstance = new ScenarioRecognizer();
|
|
257
|
+
}
|
|
258
|
+
return scenarioRecognizerInstance;
|
|
259
|
+
}
|