@weavetab/mcp 2.5.0-beta.1 → 2.5.0-beta.4
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/AGENTS.md +2 -1
- package/ARCHITECTURE.md +6 -6
- package/LICENSE +1 -1
- package/README.md +164 -802
- package/SECURITY.md +33 -22
- package/TROUBLESHOOTING.md +3 -3
- package/dist/cdp/bridge.d.ts +10 -0
- package/dist/cdp/bridge.js +285 -78
- package/dist/cdp/connector.d.ts +7 -3
- package/dist/cdp/connector.js +196 -75
- package/dist/cdp/helpers.d.ts +4 -0
- package/dist/cdp/helpers.js +181 -0
- package/dist/cdp/lock.d.ts +9 -6
- package/dist/cdp/lock.js +72 -59
- package/dist/cdp/omni_broker.d.ts +2 -1
- package/dist/cdp/omni_broker.js +13 -7
- package/dist/cdp/profiles.d.ts +10 -0
- package/dist/cdp/profiles.js +65 -0
- package/dist/cdp/walker.d.ts +25 -1
- package/dist/cdp/walker.js +354 -7
- package/dist/cli/plugin.d.ts +0 -2
- package/dist/cli/plugin.js +51 -696
- package/dist/cli/updater.d.ts +1 -1
- package/dist/cli/updater.js +27 -7
- package/dist/config/loader.d.ts +33 -6
- package/dist/config/loader.js +174 -22
- package/dist/desktop/discovery.d.ts +44 -0
- package/dist/desktop/discovery.js +364 -0
- package/dist/index.js +140 -64
- package/dist/intelligence/marks.d.ts +36 -0
- package/dist/intelligence/marks.js +30 -0
- package/dist/intelligence/patternCompiler.d.ts +118 -0
- package/dist/intelligence/patternCompiler.js +259 -0
- package/dist/intelligence/sessionMemory.d.ts +2 -0
- package/dist/intelligence/sessionMemory.js +18 -8
- package/dist/overlay/ghost.js +2 -2
- package/dist/overlay/injector.js +3 -5
- package/dist/plugin/extension_bridge.d.ts +40 -0
- package/dist/plugin/extension_bridge.js +158 -0
- package/dist/plugin/interface.d.ts +151 -15
- package/dist/plugin/loader.d.ts +19 -1
- package/dist/plugin/loader.js +331 -63
- package/dist/plugin/pipeline.d.ts +75 -0
- package/dist/plugin/pipeline.js +183 -0
- package/dist/plugin/registry.js +12 -1
- package/dist/plugin/storage.d.ts +51 -0
- package/dist/plugin/storage.js +164 -0
- package/dist/security/secrets.d.ts +1 -0
- package/dist/security/secrets.js +13 -0
- package/dist/sensors/actionability.js +1 -2
- package/dist/sensors/guard.js +4 -10
- package/dist/sensors/mutations.js +12 -0
- package/dist/sensors/operation_monitor.js +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +600 -236
- package/dist/state/agent.d.ts +1 -0
- package/dist/state/agent.js +3 -0
- package/dist/state/delta.d.ts +12 -4
- package/dist/state/delta.js +67 -14
- package/dist/state/session.d.ts +8 -2
- package/dist/state/session.js +60 -13
- package/dist/tools/browser_detect.d.ts +16 -2
- package/dist/tools/browser_detect.js +54 -6
- package/dist/tools/burst.d.ts +17 -7
- package/dist/tools/burst.js +240 -83
- package/dist/tools/checkpoint.d.ts +38 -0
- package/dist/tools/checkpoint.js +271 -0
- package/dist/tools/click.d.ts +1 -0
- package/dist/tools/click.js +97 -98
- package/dist/tools/clipboard.js +31 -24
- package/dist/tools/design_extract.js +1 -1
- package/dist/tools/eval.js +1 -1
- package/dist/tools/fill.d.ts +20 -1
- package/dist/tools/fill.js +199 -40
- package/dist/tools/find.d.ts +16 -0
- package/dist/tools/find.js +67 -44
- package/dist/tools/github/index.d.ts +19 -4
- package/dist/tools/github/index.js +40 -4
- package/dist/tools/highlight.d.ts +6 -2
- package/dist/tools/highlight.js +158 -72
- package/dist/tools/inspect.js +1 -1
- package/dist/tools/key.js +65 -56
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +57 -44
- package/dist/tools/pdf.js +21 -13
- package/dist/tools/pointer.d.ts +18 -3
- package/dist/tools/pointer.js +89 -1
- package/dist/tools/read.d.ts +28 -8
- package/dist/tools/read.js +230 -56
- package/dist/tools/scrape.js +67 -13
- package/dist/tools/screenshot.js +21 -16
- package/dist/tools/scroll.js +6 -11
- package/dist/tools/select.js +8 -2
- package/dist/tools/snapshot.js +3 -34
- package/dist/tools/tabs.d.ts +10 -10
- package/dist/tools/tabs.js +55 -39
- package/dist/tools/thought.d.ts +2 -0
- package/dist/tools/thought.js +13 -14
- package/dist/tools/type.d.ts +2 -0
- package/dist/tools/type.js +109 -60
- package/dist/tools/type_secret.js +42 -76
- package/dist/tools/upload.js +1 -1
- package/dist/tools/video_compiler.d.ts +1 -1
- package/dist/tools/video_compiler.js +11 -12
- package/dist/version.d.ts +5 -0
- package/dist/version.js +5 -0
- package/docs/DOCUMENTATION.md +82 -36
- package/docs/PLUGINS.md +82 -499
- package/docs/TOOLS.md +51 -99
- package/docs/architecture/blind-injection.md +83 -0
- package/docs/architecture/dual-injection.md +32 -0
- package/docs/architecture/overview.md +49 -0
- package/docs/architecture/security.md +35 -0
- package/docs/architecture/transport.md +37 -0
- package/docs/captcha-stealth/captcha-solver.md +75 -0
- package/docs/captcha-stealth/stealth-physics.md +60 -0
- package/docs/cli/cli-commands.md +44 -0
- package/docs/cli/cli-flags.md +20 -0
- package/docs/cli/cli-introduction.md +20 -0
- package/docs/cli/cli-scripting.md +32 -0
- package/docs/cli/g-ide/ide-antigravity.md +24 -0
- package/docs/cli/g-ide/ide-claude-code.md +24 -0
- package/docs/cli/g-ide/ide-claude-desktop.md +32 -0
- package/docs/cli/g-ide/ide-cursor.md +31 -0
- package/docs/cli/g-ide/ide-generic.md +34 -0
- package/docs/cli/g-ide/ide-opencode.md +24 -0
- package/docs/cli/g-ide/ide-vscode-copilot.md +24 -0
- package/docs/cli/g-ide/ide-windsurf.md +24 -0
- package/docs/configuration/config-locations.md +24 -0
- package/docs/configuration/configuration.md +73 -0
- package/docs/configuration/env-variables.md +19 -0
- package/docs/configuration/folders.md +55 -0
- package/docs/desktop-automation/electron-apps.md +217 -0
- package/docs/get-started/installation.md +47 -0
- package/docs/get-started/introduction.md +60 -0
- package/docs/get-started/quickstart.md +54 -0
- package/docs/get-started/setup-wizard.md +26 -0
- package/docs/intelligence/site-profiles.md +151 -0
- package/docs/skills-ecosystem/skills-overview.md +35 -0
- package/docs/skills-ecosystem/skills-recipes.md +36 -0
- package/docs/tools/tools.md +631 -0
- package/docs/troubleshooting/trouble-browser.md +27 -0
- package/docs/troubleshooting/trouble-config.md +20 -0
- package/docs/troubleshooting/trouble-loop.md +31 -0
- package/ext-dist/background.js +10 -62
- package/ext-dist/content.js +333 -921
- package/ext-dist/hud/hud.js +9 -9
- package/ext-dist/manifest.json +2 -6
- package/ext-dist/styles.css +6 -1
- package/mcp.json +108 -72
- package/package.json +14 -9
- package/server.json +3 -3
- package/docs/DEV_MODE.md +0 -68
- package/docs/dev_plugin.md +0 -310
- package/ext-dist/sidepanel/sidepanel.js +0 -25
- package/ext-dist/sidepanel.html +0 -547
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — intelligence/patternCompiler.ts
|
|
3
|
+
*
|
|
4
|
+
* Compiles raw browser session trails (action sequences) into persistent,
|
|
5
|
+
* reusable site workflow patterns stored under ~/.weavetab-system/memory/sites/.
|
|
6
|
+
*
|
|
7
|
+
* Design invariants:
|
|
8
|
+
* - One file per origin (sanitized hostname). Never creates per-path or per-run files.
|
|
9
|
+
* - Pattern names are caller-supplied (snake_case). Duplicate calls for the same
|
|
10
|
+
* name on the same origin UPDATE the existing pattern rather than creating a new file.
|
|
11
|
+
* - Volatile / unstable selectors (auto-generated classes, nth-child paths) are
|
|
12
|
+
* stripped and replaced with semantic anchors where possible.
|
|
13
|
+
* - Never reads or writes to ~/.weavetab/ (user config space).
|
|
14
|
+
*/
|
|
15
|
+
import { readFile, writeFile } from "fs/promises";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
import fs from "fs";
|
|
18
|
+
import { getSystemMemorySitesDir } from "../config/loader.js";
|
|
19
|
+
import { logDebug } from "../audit/logger.js";
|
|
20
|
+
// ─── Volatile selector detection ────────────────────────────────────────────
|
|
21
|
+
const VOLATILE_PATTERNS = [
|
|
22
|
+
/\[class\*="[a-z0-9]+-[a-z0-9]+"\]/, // CSS-module hash attribute selectors
|
|
23
|
+
/:nth-child\(\d+\)/, // positional pseudo-selectors
|
|
24
|
+
/\.\w{8,}[-_]\w{4,}/, // hashed Tailwind / CSS-module class combos
|
|
25
|
+
/div > div > div > /, // deep anonymous structural chains (3+ levels)
|
|
26
|
+
];
|
|
27
|
+
function isVolatileSelector(sel) {
|
|
28
|
+
return VOLATILE_PATTERNS.some((rx) => rx.test(sel));
|
|
29
|
+
}
|
|
30
|
+
function stabilizeSelector(sel) {
|
|
31
|
+
if (!sel)
|
|
32
|
+
return undefined;
|
|
33
|
+
if (isVolatileSelector(sel)) {
|
|
34
|
+
logDebug(`[PatternCompiler] Dropping volatile selector: ${sel}`);
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
return sel;
|
|
38
|
+
}
|
|
39
|
+
// ─── File path helpers ───────────────────────────────────────────────────────
|
|
40
|
+
function originToFilename(origin) {
|
|
41
|
+
return origin
|
|
42
|
+
.replace(/^https?:\/\//, "")
|
|
43
|
+
.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
44
|
+
}
|
|
45
|
+
function siteProfilePath(origin) {
|
|
46
|
+
const sitesDir = getSystemMemorySitesDir();
|
|
47
|
+
return join(sitesDir, `${originToFilename(origin)}.json`);
|
|
48
|
+
}
|
|
49
|
+
// ─── In-process LRU cache ───────────────────────────────────────────────────
|
|
50
|
+
const profileCache = new Map();
|
|
51
|
+
const CACHE_MAX = 100;
|
|
52
|
+
function cacheSet(origin, profile) {
|
|
53
|
+
if (profileCache.size >= CACHE_MAX && !profileCache.has(origin)) {
|
|
54
|
+
const firstKey = profileCache.keys().next().value;
|
|
55
|
+
if (firstKey !== undefined)
|
|
56
|
+
profileCache.delete(firstKey);
|
|
57
|
+
}
|
|
58
|
+
profileCache.set(origin, profile);
|
|
59
|
+
}
|
|
60
|
+
export function clearSiteProfileCache() {
|
|
61
|
+
profileCache.clear();
|
|
62
|
+
}
|
|
63
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
64
|
+
/**
|
|
65
|
+
* Load a site profile from disk. Returns null if the site has never been profiled.
|
|
66
|
+
* Hits in-process cache first — no redundant disk reads within a session.
|
|
67
|
+
*/
|
|
68
|
+
export async function getSiteProfile(origin) {
|
|
69
|
+
if (profileCache.has(origin))
|
|
70
|
+
return profileCache.get(origin);
|
|
71
|
+
const filePath = siteProfilePath(origin);
|
|
72
|
+
if (!fs.existsSync(filePath))
|
|
73
|
+
return null;
|
|
74
|
+
try {
|
|
75
|
+
const raw = await readFile(filePath, "utf-8");
|
|
76
|
+
const profile = JSON.parse(raw);
|
|
77
|
+
cacheSet(origin, profile);
|
|
78
|
+
return profile;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
logDebug(`[PatternCompiler] Failed to parse site profile for ${origin}: ${e}`);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Persist a site profile. Always deep-merges into existing data on disk — never
|
|
87
|
+
* blindly overwrites — so concurrent tool calls don't clobber each other.
|
|
88
|
+
*/
|
|
89
|
+
export async function saveSiteProfile(profile) {
|
|
90
|
+
const filePath = siteProfilePath(profile.origin);
|
|
91
|
+
let base = null;
|
|
92
|
+
if (fs.existsSync(filePath)) {
|
|
93
|
+
try {
|
|
94
|
+
base = JSON.parse(await readFile(filePath, "utf-8"));
|
|
95
|
+
}
|
|
96
|
+
catch { /* treat corrupt file as empty */ }
|
|
97
|
+
}
|
|
98
|
+
const merged = {
|
|
99
|
+
...base,
|
|
100
|
+
...profile,
|
|
101
|
+
origin: profile.origin,
|
|
102
|
+
patterns: { ...(base?.patterns ?? {}), ...profile.patterns },
|
|
103
|
+
quirks: { ...(base?.quirks ?? {}), ...profile.quirks },
|
|
104
|
+
visitCount: profile.visitCount ?? base?.visitCount ?? 0,
|
|
105
|
+
updatedAt: new Date().toISOString(),
|
|
106
|
+
};
|
|
107
|
+
cacheSet(profile.origin, merged);
|
|
108
|
+
await writeFile(filePath, JSON.stringify(merged, null, 2), "utf-8");
|
|
109
|
+
logDebug(`[PatternCompiler] Saved site profile → ${filePath}`);
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Learn or update a named workflow pattern for a given origin.
|
|
113
|
+
* - Duplicate name = merge/update, never duplicate file.
|
|
114
|
+
* - Volatile selectors in steps are dropped to keep patterns stable.
|
|
115
|
+
* - Anti-spam: skips disk write when content is identical to existing.
|
|
116
|
+
*/
|
|
117
|
+
export async function learnPattern(origin, name, incoming) {
|
|
118
|
+
const profile = (await getSiteProfile(origin)) ?? {
|
|
119
|
+
origin,
|
|
120
|
+
quirks: {},
|
|
121
|
+
patterns: {},
|
|
122
|
+
visitCount: 0,
|
|
123
|
+
updatedAt: new Date().toISOString(),
|
|
124
|
+
};
|
|
125
|
+
const existing = profile.patterns[name] ?? {
|
|
126
|
+
name,
|
|
127
|
+
steps: [],
|
|
128
|
+
reliability: 1.0,
|
|
129
|
+
strikes: 0,
|
|
130
|
+
last_verified: new Date().toISOString(),
|
|
131
|
+
total_executions: 0,
|
|
132
|
+
successful_executions: 0,
|
|
133
|
+
};
|
|
134
|
+
const stabilizedSteps = (incoming.steps ?? existing.steps).map((step) => ({
|
|
135
|
+
...step,
|
|
136
|
+
target: step.target ? stabilizeSelector(step.target) : undefined,
|
|
137
|
+
}));
|
|
138
|
+
const hasChanges = (!profile.patterns[name] ||
|
|
139
|
+
(incoming.steps !== undefined && JSON.stringify(stabilizedSteps) !== JSON.stringify(existing.steps)) ||
|
|
140
|
+
(incoming.success_indicator !== undefined && incoming.success_indicator !== existing.success_indicator) ||
|
|
141
|
+
(incoming.entry !== undefined && incoming.entry !== existing.entry));
|
|
142
|
+
if (hasChanges) {
|
|
143
|
+
const updated = {
|
|
144
|
+
...existing,
|
|
145
|
+
...incoming,
|
|
146
|
+
name,
|
|
147
|
+
steps: stabilizedSteps,
|
|
148
|
+
last_verified: new Date().toISOString(),
|
|
149
|
+
};
|
|
150
|
+
await saveSiteProfile({
|
|
151
|
+
...profile,
|
|
152
|
+
patterns: { ...profile.patterns, [name]: updated },
|
|
153
|
+
});
|
|
154
|
+
logDebug(`[PatternCompiler] Pattern "${name}" learned/updated for ${origin}`);
|
|
155
|
+
return updated;
|
|
156
|
+
}
|
|
157
|
+
else {
|
|
158
|
+
logDebug(`[PatternCompiler] Pattern "${name}" unchanged — skipping write`);
|
|
159
|
+
return existing;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Record the outcome of executing a named pattern.
|
|
164
|
+
* Updates reliability via EWMA (α=0.15) and tracks consecutive strikes.
|
|
165
|
+
* 3 strikes → reliability will naturally fall below 0.4, agent should re-explore.
|
|
166
|
+
*/
|
|
167
|
+
export async function recordPatternExecution(origin, patternName, success, durationMs) {
|
|
168
|
+
const profile = await getSiteProfile(origin);
|
|
169
|
+
if (!profile)
|
|
170
|
+
return null;
|
|
171
|
+
const pattern = profile.patterns[patternName];
|
|
172
|
+
if (!pattern)
|
|
173
|
+
return null;
|
|
174
|
+
const alpha = 0.15;
|
|
175
|
+
const newReliability = Math.round((0.85 * pattern.reliability + alpha * (success ? 1 : 0)) * 1000) / 1000;
|
|
176
|
+
const updated = {
|
|
177
|
+
...pattern,
|
|
178
|
+
reliability: newReliability,
|
|
179
|
+
strikes: success ? 0 : pattern.strikes + 1,
|
|
180
|
+
last_verified: success ? new Date().toISOString() : pattern.last_verified,
|
|
181
|
+
total_executions: pattern.total_executions + 1,
|
|
182
|
+
successful_executions: success ? pattern.successful_executions + 1 : pattern.successful_executions,
|
|
183
|
+
avg_duration_ms: durationMs !== undefined
|
|
184
|
+
? Math.round(pattern.avg_duration_ms !== undefined
|
|
185
|
+
? 0.8 * pattern.avg_duration_ms + 0.2 * durationMs
|
|
186
|
+
: durationMs)
|
|
187
|
+
: pattern.avg_duration_ms,
|
|
188
|
+
};
|
|
189
|
+
if (updated.strikes >= 3) {
|
|
190
|
+
logDebug(`[PatternCompiler] ⚠ Pattern "${patternName}" on ${origin} hit 3 strikes (reliability: ${updated.reliability}) — re-learning recommended`);
|
|
191
|
+
}
|
|
192
|
+
await saveSiteProfile({
|
|
193
|
+
...profile,
|
|
194
|
+
patterns: { ...profile.patterns, [patternName]: updated },
|
|
195
|
+
});
|
|
196
|
+
return updated;
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Record or update a site quirk (cookie banner, rate limit, modal popup, etc.)
|
|
200
|
+
* Only writes if the stored data differs from incoming — prevents spam writes.
|
|
201
|
+
*/
|
|
202
|
+
export async function recordQuirk(origin, quirkKey, quirkData) {
|
|
203
|
+
const profile = (await getSiteProfile(origin)) ?? {
|
|
204
|
+
origin,
|
|
205
|
+
quirks: {},
|
|
206
|
+
patterns: {},
|
|
207
|
+
visitCount: 0,
|
|
208
|
+
updatedAt: new Date().toISOString(),
|
|
209
|
+
};
|
|
210
|
+
const existing = profile.quirks[quirkKey];
|
|
211
|
+
if (JSON.stringify(existing) === JSON.stringify(quirkData)) {
|
|
212
|
+
logDebug(`[PatternCompiler] Quirk "${quirkKey}" unchanged — skipping write`);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
await saveSiteProfile({
|
|
216
|
+
...profile,
|
|
217
|
+
quirks: { ...profile.quirks, [quirkKey]: quirkData },
|
|
218
|
+
});
|
|
219
|
+
logDebug(`[PatternCompiler] Recorded quirk "${quirkKey}" for ${origin}`);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Returns the reliability score (0–1) of a named pattern, or -1 if unknown.
|
|
223
|
+
*/
|
|
224
|
+
export async function getPatternReliability(origin, patternName) {
|
|
225
|
+
const profile = await getSiteProfile(origin);
|
|
226
|
+
if (!profile)
|
|
227
|
+
return -1;
|
|
228
|
+
return profile.patterns[patternName]?.reliability ?? -1;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Formats a compact site profile hint string for agent system prompt injection.
|
|
232
|
+
* Called at session start when navigating to a known origin.
|
|
233
|
+
*/
|
|
234
|
+
export function formatSiteProfileHint(profile) {
|
|
235
|
+
const lines = [`[SiteProfile] ${profile.origin}`];
|
|
236
|
+
if (profile.framework)
|
|
237
|
+
lines.push(` framework: ${profile.framework}`);
|
|
238
|
+
if (profile.captcha_frequency && profile.captcha_frequency !== "none") {
|
|
239
|
+
lines.push(` captcha_frequency: ${profile.captcha_frequency}`);
|
|
240
|
+
}
|
|
241
|
+
const patternNames = Object.keys(profile.patterns);
|
|
242
|
+
if (patternNames.length > 0) {
|
|
243
|
+
lines.push(" known_patterns:");
|
|
244
|
+
for (const name of patternNames) {
|
|
245
|
+
const p = profile.patterns[name];
|
|
246
|
+
const flag = p.strikes >= 3
|
|
247
|
+
? " [RE-LEARN NEEDED]"
|
|
248
|
+
: p.reliability >= 0.8
|
|
249
|
+
? " [fast-path eligible]"
|
|
250
|
+
: "";
|
|
251
|
+
lines.push(` - ${name}: reliability=${p.reliability.toFixed(2)}, steps=${p.steps.length}${flag}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const quirkKeys = Object.keys(profile.quirks).filter((k) => profile.quirks[k] !== undefined && profile.quirks[k] !== null);
|
|
255
|
+
if (quirkKeys.length > 0) {
|
|
256
|
+
lines.push(` quirks: ${quirkKeys.join(", ")}`);
|
|
257
|
+
}
|
|
258
|
+
return lines.join("\n");
|
|
259
|
+
}
|
|
@@ -34,6 +34,8 @@ export interface DomainProfile {
|
|
|
34
34
|
updatedAt: string;
|
|
35
35
|
visitCount: number;
|
|
36
36
|
}
|
|
37
|
+
export declare function clearMemoryCache(): void;
|
|
38
|
+
export declare function getDomainFilePath(hostname: string): string;
|
|
37
39
|
export declare function loadDomainProfile(hostname: string): Promise<DomainProfile | null>;
|
|
38
40
|
export declare function saveDomainProfile(hostname: string, updates: Partial<DomainProfile>): Promise<DomainProfile>;
|
|
39
41
|
/**
|
|
@@ -1,20 +1,30 @@
|
|
|
1
1
|
import { readFile, writeFile } from "fs/promises";
|
|
2
2
|
import { join } from "path";
|
|
3
|
-
import { GLOBAL_CONFIG_DIR } from "../config/loader.js";
|
|
3
|
+
import { GLOBAL_CONFIG_DIR, getSystemDir } from "../config/loader.js";
|
|
4
4
|
import { logDebug } from "../audit/logger.js";
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
function domainFile(hostname) {
|
|
7
7
|
const safe = hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
8
|
-
const memoryDir =
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
const memoryDir = getSystemDir("memory");
|
|
9
|
+
const systemPath = join(memoryDir, `${safe}.json`);
|
|
10
|
+
// Backward compatibility: migrate legacy ~/.weavetab/memory/<safe>.json if it exists
|
|
11
|
+
const legacyPath = join(GLOBAL_CONFIG_DIR, "memory", `${safe}.json`);
|
|
12
|
+
if (!fs.existsSync(systemPath) && fs.existsSync(legacyPath)) {
|
|
13
|
+
try {
|
|
14
|
+
fs.copyFileSync(legacyPath, systemPath);
|
|
15
|
+
}
|
|
16
|
+
catch { /* ignore migration error */ }
|
|
17
|
+
}
|
|
18
|
+
return systemPath;
|
|
12
19
|
}
|
|
13
|
-
// memoryCache is capped at 200 DomainProfile entries (one per unique hostname).
|
|
14
|
-
// When the cap is hit, the oldest entry is evicted (insertion-order LRU) so the
|
|
15
|
-
// cache can't grow without bound over a long session.
|
|
16
20
|
const memoryCache = new Map();
|
|
17
21
|
const MEMORY_CACHE_MAX = 200;
|
|
22
|
+
export function clearMemoryCache() {
|
|
23
|
+
memoryCache.clear();
|
|
24
|
+
}
|
|
25
|
+
export function getDomainFilePath(hostname) {
|
|
26
|
+
return domainFile(hostname);
|
|
27
|
+
}
|
|
18
28
|
function memoryCacheSet(hostname, profile) {
|
|
19
29
|
if (memoryCache.size >= MEMORY_CACHE_MAX && !memoryCache.has(hostname)) {
|
|
20
30
|
// Evict the oldest (first) entry
|
package/dist/overlay/ghost.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logDebug } from "../audit/logger.js";
|
|
2
|
-
import { getConfigHeadless
|
|
2
|
+
import { getConfigHeadless } from "../config/loader.js";
|
|
3
3
|
function sleep(ms) {
|
|
4
4
|
return new Promise(r => setTimeout(r, ms));
|
|
5
5
|
}
|
|
@@ -378,7 +378,7 @@ export async function ghostMove(session, targetX, targetY, config, label, profil
|
|
|
378
378
|
return;
|
|
379
379
|
}
|
|
380
380
|
wtCursor(session, currentX, currentY, label && distance >= 150 ? "searching" : "idle", cursorOpts);
|
|
381
|
-
if (label && distance >= 150
|
|
381
|
+
if (label && distance >= 150) {
|
|
382
382
|
wtThought(session, `moving to ${label.slice(0, 40)}...`);
|
|
383
383
|
await sleep(60 + Math.random() * 80);
|
|
384
384
|
}
|
package/dist/overlay/injector.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { logDebug } from "../audit/logger.js";
|
|
2
|
-
import {
|
|
2
|
+
import { getConfigDevMode } from "../config/loader.js";
|
|
3
3
|
// ─── Minimal Overlay Script ───────────────────────────────────────────────────
|
|
4
4
|
export function getMinimalOverlayScript() {
|
|
5
5
|
return `(function(){
|
|
@@ -67,7 +67,6 @@ export function getMinimalOverlayScript() {
|
|
|
67
67
|
|
|
68
68
|
window.__wt_setConfig = function(c) {
|
|
69
69
|
if (c && c.devMode) window.__wt_devMode = true;
|
|
70
|
-
if (c && c.visuals !== undefined) window.__wt_showVisuals = !!c.visuals;
|
|
71
70
|
};
|
|
72
71
|
|
|
73
72
|
window.__wt_status = function() { return JSON.stringify({ rootInjected: true }); };
|
|
@@ -121,7 +120,6 @@ function buildConfigExpr(config) {
|
|
|
121
120
|
return `
|
|
122
121
|
if(window.__wt_setConfig)window.__wt_setConfig(${JSON.stringify({
|
|
123
122
|
...config,
|
|
124
|
-
visuals: getConfigVisuals(config) !== false,
|
|
125
123
|
})});
|
|
126
124
|
window.postMessage({type:'Weavetab-msg-in',data:${JSON.stringify(msg)}},'*');
|
|
127
125
|
`;
|
|
@@ -154,7 +152,7 @@ export async function ensureVisualLayer(session, config, client) {
|
|
|
154
152
|
returnByValue: true
|
|
155
153
|
});
|
|
156
154
|
if (checkResult?.value) {
|
|
157
|
-
logDebug(`[Weavetab]
|
|
155
|
+
logDebug(`[Weavetab] overlay injected`);
|
|
158
156
|
}
|
|
159
157
|
else {
|
|
160
158
|
logDebug(`[Weavetab] [!] Minimal overlay injection failed - element not found`);
|
|
@@ -173,7 +171,7 @@ export async function setupOverlay(session, config) {
|
|
|
173
171
|
await ensureVisualLayer(session, config);
|
|
174
172
|
}
|
|
175
173
|
export async function setFaviconState(_session, _state) {
|
|
176
|
-
//
|
|
174
|
+
// handled via extension content.js
|
|
177
175
|
}
|
|
178
176
|
export async function removeTitleBadge(_session) {
|
|
179
177
|
// No-op: title dot prefix removed in favor of favicon indicators
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type CDP from "chrome-remote-interface";
|
|
2
|
+
import type { WeavetabPermissions } from "./interface.js";
|
|
3
|
+
export interface IPluginExtensionBridge {
|
|
4
|
+
send(session: CDP.Client, message: object): Promise<boolean>;
|
|
5
|
+
setHudState(session: CDP.Client, state: {
|
|
6
|
+
action?: string;
|
|
7
|
+
badge?: string;
|
|
8
|
+
badgeColor?: string;
|
|
9
|
+
groupState?: string;
|
|
10
|
+
}): Promise<void>;
|
|
11
|
+
showThought(session: CDP.Client, text: string, options?: {
|
|
12
|
+
durationMs?: number;
|
|
13
|
+
}): Promise<void>;
|
|
14
|
+
clearThought(session: CDP.Client): Promise<void>;
|
|
15
|
+
injectStyles(session: CDP.Client, css: string): Promise<void>;
|
|
16
|
+
injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
|
|
17
|
+
hideOverlay(session: CDP.Client): Promise<void>;
|
|
18
|
+
restoreOverlay(session: CDP.Client): Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
export declare class PluginExtensionBridge implements IPluginExtensionBridge {
|
|
21
|
+
readonly pluginName: string;
|
|
22
|
+
private readonly permissions?;
|
|
23
|
+
constructor(pluginName: string, permissions?: WeavetabPermissions | undefined);
|
|
24
|
+
private checkPermission;
|
|
25
|
+
send(session: CDP.Client, message: object): Promise<boolean>;
|
|
26
|
+
setHudState(session: CDP.Client, state: {
|
|
27
|
+
action?: string;
|
|
28
|
+
badge?: string;
|
|
29
|
+
badgeColor?: string;
|
|
30
|
+
groupState?: string;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
showThought(session: CDP.Client, text: string, options?: {
|
|
33
|
+
durationMs?: number;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
clearThought(session: CDP.Client): Promise<void>;
|
|
36
|
+
injectStyles(session: CDP.Client, css: string): Promise<void>;
|
|
37
|
+
injectScript<T = any>(session: CDP.Client, js: string): Promise<T>;
|
|
38
|
+
hideOverlay(session: CDP.Client): Promise<void>;
|
|
39
|
+
restoreOverlay(session: CDP.Client): Promise<void>;
|
|
40
|
+
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { sendToExtension, hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
|
|
2
|
+
import { logDebug } from "../audit/logger.js";
|
|
3
|
+
export class PluginExtensionBridge {
|
|
4
|
+
pluginName;
|
|
5
|
+
permissions;
|
|
6
|
+
constructor(pluginName, permissions) {
|
|
7
|
+
this.pluginName = pluginName;
|
|
8
|
+
this.permissions = permissions;
|
|
9
|
+
}
|
|
10
|
+
checkPermission(category) {
|
|
11
|
+
if (!this.permissions || !this.permissions.extension)
|
|
12
|
+
return true; // Default permissive if not restricted
|
|
13
|
+
return this.permissions.extension[category] !== false;
|
|
14
|
+
}
|
|
15
|
+
async send(session, message) {
|
|
16
|
+
if (!session)
|
|
17
|
+
return false;
|
|
18
|
+
return await sendToExtension(session, {
|
|
19
|
+
...message,
|
|
20
|
+
plugin: this.pluginName,
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
async setHudState(session, state) {
|
|
24
|
+
if (!this.checkPermission("hud")) {
|
|
25
|
+
logDebug(`[Weavetab PluginExtension] Plugin "${this.pluginName}" lacks permissions.extension.hud`);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
await this.send(session, {
|
|
29
|
+
type: "wt-hud-update",
|
|
30
|
+
action: state.action,
|
|
31
|
+
badge: state.badge,
|
|
32
|
+
badgeColor: state.badgeColor,
|
|
33
|
+
groupState: state.groupState,
|
|
34
|
+
timestamp: Date.now(),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
async showThought(session, text, options) {
|
|
38
|
+
if (!this.checkPermission("hud"))
|
|
39
|
+
return;
|
|
40
|
+
if (!session || !session.Runtime)
|
|
41
|
+
return;
|
|
42
|
+
const duration = options?.durationMs || 4000;
|
|
43
|
+
const safeText = JSON.stringify(text);
|
|
44
|
+
try {
|
|
45
|
+
await session.Runtime.evaluate({
|
|
46
|
+
expression: `(function() {
|
|
47
|
+
var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
|
|
48
|
+
if (existing) existing.remove();
|
|
49
|
+
|
|
50
|
+
var bubble = document.createElement('div');
|
|
51
|
+
bubble.id = '__wt_plugin_thought_${this.pluginName}';
|
|
52
|
+
bubble.className = 'wt-bubble';
|
|
53
|
+
bubble.setAttribute('data-wt-internal', 'true');
|
|
54
|
+
bubble.style.cssText = [
|
|
55
|
+
'position: fixed',
|
|
56
|
+
'bottom: 24px',
|
|
57
|
+
'right: 24px',
|
|
58
|
+
'background: linear-gradient(135deg, rgba(17, 24, 39, 0.95), rgba(30, 41, 59, 0.95))',
|
|
59
|
+
'color: #f8fafc',
|
|
60
|
+
'padding: 12px 18px',
|
|
61
|
+
'border-radius: 12px',
|
|
62
|
+
'font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
|
|
63
|
+
'font-size: 13px',
|
|
64
|
+
'font-weight: 500',
|
|
65
|
+
'box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 255, 255, 0.1)',
|
|
66
|
+
'z-index: 2147483647',
|
|
67
|
+
'backdrop-filter: blur(8px)',
|
|
68
|
+
'pointer-events: none',
|
|
69
|
+
'animation: wtThoughtFade 0.25s cubic-bezier(0.16, 1, 0.3, 1)',
|
|
70
|
+
'display: flex',
|
|
71
|
+
'align-items: center',
|
|
72
|
+
'gap: 8px'
|
|
73
|
+
].join(';');
|
|
74
|
+
|
|
75
|
+
var icon = document.createElement('span');
|
|
76
|
+
icon.textContent = '✨';
|
|
77
|
+
var textSpan = document.createElement('span');
|
|
78
|
+
textSpan.textContent = ${safeText};
|
|
79
|
+
|
|
80
|
+
bubble.appendChild(icon);
|
|
81
|
+
bubble.appendChild(textSpan);
|
|
82
|
+
document.body.appendChild(bubble);
|
|
83
|
+
|
|
84
|
+
setTimeout(function() {
|
|
85
|
+
if (bubble && bubble.parentNode) {
|
|
86
|
+
bubble.style.opacity = '0';
|
|
87
|
+
bubble.style.transition = 'opacity 0.3s ease';
|
|
88
|
+
setTimeout(function() { bubble.remove(); }, 300);
|
|
89
|
+
}
|
|
90
|
+
}, ${duration});
|
|
91
|
+
})()`,
|
|
92
|
+
returnByValue: false,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
logDebug(`[Weavetab PluginExtension] Failed to show thought:`, e);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async clearThought(session) {
|
|
100
|
+
if (!session || !session.Runtime)
|
|
101
|
+
return;
|
|
102
|
+
try {
|
|
103
|
+
await session.Runtime.evaluate({
|
|
104
|
+
expression: `(function() {
|
|
105
|
+
var existing = document.getElementById('__wt_plugin_thought_${this.pluginName}');
|
|
106
|
+
if (existing) existing.remove();
|
|
107
|
+
})()`,
|
|
108
|
+
returnByValue: false,
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch { /* ignore */ }
|
|
112
|
+
}
|
|
113
|
+
async injectStyles(session, css) {
|
|
114
|
+
if (!this.checkPermission("styles")) {
|
|
115
|
+
throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.styles`);
|
|
116
|
+
}
|
|
117
|
+
if (!session || !session.Runtime)
|
|
118
|
+
return;
|
|
119
|
+
const safeCss = JSON.stringify(css);
|
|
120
|
+
await session.Runtime.evaluate({
|
|
121
|
+
expression: `(function() {
|
|
122
|
+
var styleId = '__wt_plugin_styles_${this.pluginName}';
|
|
123
|
+
var style = document.getElementById(styleId) || document.createElement('style');
|
|
124
|
+
style.id = styleId;
|
|
125
|
+
style.textContent = ${safeCss};
|
|
126
|
+
if (!style.parentNode) document.head.appendChild(style);
|
|
127
|
+
})()`,
|
|
128
|
+
returnByValue: false,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async injectScript(session, js) {
|
|
132
|
+
if (!this.checkPermission("scripts")) {
|
|
133
|
+
throw new Error(`Permission Denied: Plugin "${this.pluginName}" does not have permissions.extension.scripts`);
|
|
134
|
+
}
|
|
135
|
+
if (!session || !session.Runtime)
|
|
136
|
+
throw new Error("No active CDP session available");
|
|
137
|
+
const res = await session.Runtime.evaluate({
|
|
138
|
+
expression: js,
|
|
139
|
+
returnByValue: true,
|
|
140
|
+
awaitPromise: true,
|
|
141
|
+
});
|
|
142
|
+
return res.result?.value ?? res;
|
|
143
|
+
}
|
|
144
|
+
async hideOverlay(session) {
|
|
145
|
+
if (!this.checkPermission("overlay"))
|
|
146
|
+
return;
|
|
147
|
+
if (!session)
|
|
148
|
+
return;
|
|
149
|
+
await hideWeaveTabOverlay(session);
|
|
150
|
+
}
|
|
151
|
+
async restoreOverlay(session) {
|
|
152
|
+
if (!this.checkPermission("overlay"))
|
|
153
|
+
return;
|
|
154
|
+
if (!session)
|
|
155
|
+
return;
|
|
156
|
+
await restoreWeaveTabOverlay(session);
|
|
157
|
+
}
|
|
158
|
+
}
|