@weavetab/mcp 2.5.0-beta.0 → 2.5.0-beta.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/AGENTS.md +1 -0
- package/LICENSE +2 -2
- package/README.md +208 -43
- package/SECURITY.md +3 -3
- package/TROUBLESHOOTING.md +1 -1
- package/dist/cdp/bridge.d.ts +10 -0
- package/dist/cdp/bridge.js +288 -68
- package/dist/cdp/connector.d.ts +5 -3
- package/dist/cdp/connector.js +188 -74
- package/dist/cdp/extension.js +0 -1
- package/dist/cdp/helpers.js +0 -2
- package/dist/cdp/lock.d.ts +9 -6
- package/dist/cdp/lock.js +72 -59
- package/dist/cdp/omni_broker.d.ts +1 -0
- package/dist/cdp/omni_broker.js +12 -6
- package/dist/cdp/profiles.d.ts +10 -0
- package/dist/cdp/profiles.js +65 -0
- package/dist/cdp/walker.js +50 -0
- package/dist/cli/plugin.d.ts +0 -2
- package/dist/cli/plugin.js +31 -683
- package/dist/cli/updater.js +2 -2
- package/dist/config/loader.d.ts +25 -6
- package/dist/config/loader.js +133 -20
- package/dist/index.js +48 -52
- package/dist/lifecycle/mission_manager.d.ts +0 -1
- package/dist/lifecycle/mission_manager.js +0 -3
- package/dist/overlay/ghost.d.ts +4 -0
- package/dist/overlay/ghost.js +118 -94
- package/dist/overlay/injector.d.ts +1 -1
- package/dist/overlay/injector.js +1 -15
- package/dist/plugin/extension_bridge.d.ts +40 -0
- package/dist/plugin/extension_bridge.js +158 -0
- package/dist/plugin/interface.d.ts +78 -16
- package/dist/plugin/loader.d.ts +4 -0
- package/dist/plugin/loader.js +178 -57
- package/dist/plugin/registry.js +11 -1
- package/dist/plugin/storage.d.ts +51 -0
- package/dist/plugin/storage.js +164 -0
- package/dist/server.js +217 -151
- package/dist/state/session.d.ts +2 -2
- package/dist/state/session.js +33 -13
- package/dist/tools/browser_detect.d.ts +2 -1
- package/dist/tools/browser_detect.js +6 -5
- package/dist/tools/burst.d.ts +1 -0
- package/dist/tools/burst.js +4 -6
- package/dist/tools/click.js +23 -28
- package/dist/tools/clipboard.js +31 -24
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/navigate.js +28 -10
- package/dist/tools/read.js +1 -1
- package/dist/tools/tabs.d.ts +10 -10
- package/dist/tools/tabs.js +47 -39
- package/dist/tools/type.js +2 -2
- package/docs/DOCUMENTATION.md +63 -6
- package/docs/architecture/blind-injection.md +20 -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 +54 -0
- package/docs/desktop-automation/electron-apps.md +162 -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/skills-ecosystem/skills-overview.md +35 -0
- package/docs/skills-ecosystem/skills-recipes.md +36 -0
- package/docs/tools/tools.md +293 -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/content.js +126 -101
- package/mcp.json +8 -15
- package/package.json +11 -5
- package/server.json +3 -3
- package/dist/tools/plan.d.ts +0 -39
- package/dist/tools/plan.js +0 -161
- package/docs/DEV_MODE.md +0 -68
- package/docs/PLUGINS.md +0 -538
- package/docs/TOOLS.md +0 -104
- package/docs/dev_plugin.md +0 -310
package/dist/cdp/lock.js
CHANGED
|
@@ -10,49 +10,56 @@ import * as path from "node:path";
|
|
|
10
10
|
import * as os from "node:os";
|
|
11
11
|
import * as net from "node:net";
|
|
12
12
|
import { lock } from "proper-lockfile";
|
|
13
|
+
import { sanitizeAppName } from "./profiles.js";
|
|
13
14
|
const LOCK_DIR = path.join(os.homedir(), ".weavetab-system", "locks");
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
//
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
15
|
+
export function getLockFilePath(appName) {
|
|
16
|
+
if (appName && appName.trim()) {
|
|
17
|
+
return path.join(LOCK_DIR, `.weavetab.electron.${sanitizeAppName(appName)}.lock`);
|
|
18
|
+
}
|
|
19
|
+
return path.join(LOCK_DIR, ".weavetab.lock");
|
|
20
|
+
}
|
|
21
|
+
export function getMutexFilePath(appName) {
|
|
22
|
+
if (appName && appName.trim()) {
|
|
23
|
+
return path.join(LOCK_DIR, `.weavetab.electron.${sanitizeAppName(appName)}.lock.mutex`);
|
|
24
|
+
}
|
|
25
|
+
return path.join(LOCK_DIR, ".weavetab.lock.mutex");
|
|
26
|
+
}
|
|
27
|
+
// ─── In-process serialization queues per app/browser ──────────────────────────
|
|
28
|
+
const _inProcessQueues = new Map();
|
|
29
|
+
const _heldDepths = new Map();
|
|
30
|
+
const _heldFileReleases = new Map();
|
|
31
|
+
const _heldSlotResolves = new Map();
|
|
32
|
+
function getTargetKey(appName) {
|
|
33
|
+
return appName && appName.trim() ? `electron:${sanitizeAppName(appName)}` : "browser";
|
|
34
|
+
}
|
|
35
|
+
async function acquireMutex(appName) {
|
|
36
|
+
const key = getTargetKey(appName);
|
|
37
|
+
const currentDepth = _heldDepths.get(key) ?? 0;
|
|
38
|
+
// Re-entrant fast path: this process already holds the mutex for this target.
|
|
39
|
+
if (currentDepth > 0) {
|
|
40
|
+
_heldDepths.set(key, currentDepth + 1);
|
|
36
41
|
return async () => {
|
|
37
|
-
|
|
42
|
+
const d = (_heldDepths.get(key) ?? 1) - 1;
|
|
43
|
+
_heldDepths.set(key, Math.max(0, d));
|
|
38
44
|
};
|
|
39
45
|
}
|
|
40
|
-
// Enqueue this caller
|
|
46
|
+
// Enqueue this caller for this specific key
|
|
41
47
|
let resolveSlot;
|
|
42
48
|
const slot = new Promise(resolve => { resolveSlot = resolve; });
|
|
43
|
-
const prevQueue =
|
|
44
|
-
|
|
45
|
-
await prevQueue; // Wait for all prior callers to fully release
|
|
49
|
+
const prevQueue = _inProcessQueues.get(key) ?? Promise.resolve();
|
|
50
|
+
_inProcessQueues.set(key, prevQueue.then(() => slot));
|
|
51
|
+
await prevQueue; // Wait for all prior callers for this key to fully release
|
|
46
52
|
fs.mkdirSync(LOCK_DIR, { recursive: true });
|
|
47
|
-
|
|
53
|
+
const mutexFile = getMutexFilePath(appName);
|
|
54
|
+
if (!fs.existsSync(mutexFile)) {
|
|
48
55
|
try {
|
|
49
|
-
fs.writeFileSync(
|
|
56
|
+
fs.writeFileSync(mutexFile, String(process.pid), "utf-8");
|
|
50
57
|
}
|
|
51
58
|
catch { /* race/non-fatal */ }
|
|
52
59
|
}
|
|
53
60
|
let fileRelease = null;
|
|
54
61
|
try {
|
|
55
|
-
fileRelease = await lock(
|
|
62
|
+
fileRelease = await lock(mutexFile, {
|
|
56
63
|
retries: {
|
|
57
64
|
retries: 25,
|
|
58
65
|
minTimeout: 200,
|
|
@@ -62,28 +69,29 @@ async function acquireMutex() {
|
|
|
62
69
|
});
|
|
63
70
|
// Write current PID to mutex file for diagnostics/HUD
|
|
64
71
|
try {
|
|
65
|
-
fs.writeFileSync(
|
|
72
|
+
fs.writeFileSync(mutexFile, String(process.pid), "utf-8");
|
|
66
73
|
}
|
|
67
74
|
catch { /* non-fatal */ }
|
|
68
75
|
}
|
|
69
76
|
catch (err) {
|
|
70
77
|
resolveSlot(); // Unblock the queue even on failure
|
|
71
|
-
throw new Error(`[Weavetab] Could not acquire launch mutex after retries: ${err?.message || err}`);
|
|
78
|
+
throw new Error(`[Weavetab] Could not acquire launch mutex for ${key} after retries: ${err?.message || err}`);
|
|
72
79
|
}
|
|
73
80
|
// Mark this process as the current holder so nested calls skip the queue.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
81
|
+
_heldDepths.set(key, 1);
|
|
82
|
+
_heldFileReleases.set(key, fileRelease);
|
|
83
|
+
_heldSlotResolves.set(key, resolveSlot);
|
|
77
84
|
// Return a release function that releases both the file lock and the in-process slot
|
|
78
85
|
return async () => {
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
const d = (_heldDepths.get(key) ?? 1) - 1;
|
|
87
|
+
_heldDepths.set(key, Math.max(0, d));
|
|
88
|
+
if (d > 0)
|
|
81
89
|
return; // nested regions still active
|
|
82
|
-
|
|
83
|
-
const rel =
|
|
84
|
-
const slotResolve =
|
|
85
|
-
|
|
86
|
-
|
|
90
|
+
_heldDepths.delete(key);
|
|
91
|
+
const rel = _heldFileReleases.get(key);
|
|
92
|
+
const slotResolve = _heldSlotResolves.get(key);
|
|
93
|
+
_heldFileReleases.delete(key);
|
|
94
|
+
_heldSlotResolves.delete(key);
|
|
87
95
|
try {
|
|
88
96
|
if (rel)
|
|
89
97
|
await rel();
|
|
@@ -94,44 +102,49 @@ async function acquireMutex() {
|
|
|
94
102
|
};
|
|
95
103
|
}
|
|
96
104
|
// ─── Public API ───────────────────────────────────────────────────────────────
|
|
97
|
-
export async function writeLockSafe(data) {
|
|
98
|
-
const
|
|
105
|
+
export async function writeLockSafe(data, appName) {
|
|
106
|
+
const targetApp = appName || data.appName;
|
|
107
|
+
const release = await acquireMutex(targetApp);
|
|
108
|
+
const lockFile = getLockFilePath(targetApp);
|
|
99
109
|
try {
|
|
100
110
|
fs.mkdirSync(LOCK_DIR, { recursive: true });
|
|
101
|
-
fs.writeFileSync(
|
|
111
|
+
fs.writeFileSync(lockFile, JSON.stringify(data, null, 2), "utf-8");
|
|
102
112
|
}
|
|
103
113
|
finally {
|
|
104
114
|
await release();
|
|
105
115
|
}
|
|
106
116
|
}
|
|
107
|
-
export async function writeLock(data) {
|
|
108
|
-
await writeLockSafe(data);
|
|
117
|
+
export async function writeLock(data, appName) {
|
|
118
|
+
await writeLockSafe(data, appName);
|
|
109
119
|
}
|
|
110
|
-
export function readLock() {
|
|
111
|
-
|
|
120
|
+
export function readLock(appName) {
|
|
121
|
+
const lockFile = getLockFilePath(appName);
|
|
122
|
+
if (!fs.existsSync(lockFile))
|
|
112
123
|
return null;
|
|
113
124
|
try {
|
|
114
|
-
return JSON.parse(fs.readFileSync(
|
|
125
|
+
return JSON.parse(fs.readFileSync(lockFile, "utf-8"));
|
|
115
126
|
}
|
|
116
127
|
catch {
|
|
117
128
|
return null;
|
|
118
129
|
}
|
|
119
130
|
}
|
|
120
|
-
export function clearLock() {
|
|
131
|
+
export function clearLock(appName) {
|
|
132
|
+
const lockFile = getLockFilePath(appName);
|
|
133
|
+
const mutexFile = getMutexFilePath(appName);
|
|
121
134
|
try {
|
|
122
|
-
if (fs.existsSync(
|
|
123
|
-
fs.unlinkSync(
|
|
135
|
+
if (fs.existsSync(lockFile))
|
|
136
|
+
fs.unlinkSync(lockFile);
|
|
124
137
|
}
|
|
125
138
|
catch (e) {
|
|
126
139
|
logDebug(`[Weavetab] [!] Failed to clear lock file: ${e}`);
|
|
127
140
|
}
|
|
128
141
|
try {
|
|
129
|
-
if (fs.existsSync(
|
|
130
|
-
fs.unlinkSync(
|
|
142
|
+
if (fs.existsSync(mutexFile))
|
|
143
|
+
fs.unlinkSync(mutexFile);
|
|
131
144
|
}
|
|
132
145
|
catch { /* ignore */ }
|
|
133
146
|
try {
|
|
134
|
-
const lockDir =
|
|
147
|
+
const lockDir = mutexFile + ".lock";
|
|
135
148
|
if (fs.existsSync(lockDir))
|
|
136
149
|
fs.rmSync(lockDir, { recursive: true, force: true });
|
|
137
150
|
}
|
|
@@ -142,14 +155,14 @@ export function clearLock() {
|
|
|
142
155
|
* Prevents a second MCP server instance from wiping the first instance's browser lock
|
|
143
156
|
* during its retry loop, which would allow a race condition where two browsers spawn.
|
|
144
157
|
*/
|
|
145
|
-
export function clearLockIfOwned() {
|
|
158
|
+
export function clearLockIfOwned(appName) {
|
|
146
159
|
try {
|
|
147
|
-
const lock = readLock();
|
|
160
|
+
const lock = readLock(appName);
|
|
148
161
|
if (!lock || lock.pid !== process.pid) {
|
|
149
162
|
logDebug(`[Weavetab] clearLockIfOwned: skipping — lock owned by PID ${lock?.pid ?? 'none'}, this PID is ${process.pid}`);
|
|
150
163
|
return;
|
|
151
164
|
}
|
|
152
|
-
clearLock();
|
|
165
|
+
clearLock(appName);
|
|
153
166
|
}
|
|
154
167
|
catch (e) {
|
|
155
168
|
logDebug(`[Weavetab] clearLockIfOwned error: ${e}`);
|
|
@@ -14,4 +14,5 @@ export declare function initOmniBroker(parentSession: CDP.Client, parentTargetId
|
|
|
14
14
|
* Executes a CDP command across all known iframe contexts if it fails in the top-level context.
|
|
15
15
|
*/
|
|
16
16
|
export declare function omniEvaluate(parentSession: CDP.Client, parentTargetId: string, expression: string, returnByValue?: boolean): Promise<any>;
|
|
17
|
+
export declare function getOmniSessions(parentTargetId: string): Map<string, CDP.Client> | undefined;
|
|
17
18
|
export declare function cleanupOmniBroker(parentTargetId: string): void;
|
package/dist/cdp/omni_broker.js
CHANGED
|
@@ -20,13 +20,16 @@ export async function initOmniBroker(parentSession, parentTargetId, port) {
|
|
|
20
20
|
}
|
|
21
21
|
parentSession.Target.on("attachedToTarget", async (event) => {
|
|
22
22
|
const { targetInfo } = event;
|
|
23
|
-
if (targetInfo.type === 'iframe') {
|
|
23
|
+
if (targetInfo && targetInfo.targetId !== parentTargetId && (targetInfo.type === 'iframe' || targetInfo.type === 'webview' || targetInfo.type === 'page' || targetInfo.type === 'app')) {
|
|
24
24
|
try {
|
|
25
|
-
// Connect a flat session to the iframe target
|
|
26
|
-
const
|
|
27
|
-
await
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
// Connect a flat session to the child iframe/webview target
|
|
26
|
+
const childSession = await CDP({ host: "127.0.0.1", port, target: targetInfo.targetId });
|
|
27
|
+
await Promise.all([
|
|
28
|
+
childSession.Runtime.enable().catch(() => { }),
|
|
29
|
+
childSession.DOM.enable().catch(() => { }),
|
|
30
|
+
childSession.Page.enable().catch(() => { })
|
|
31
|
+
]);
|
|
32
|
+
omniSessions.get(parentTargetId).set(targetInfo.targetId, childSession);
|
|
30
33
|
}
|
|
31
34
|
catch (e) {
|
|
32
35
|
// Fallback or ignore
|
|
@@ -105,6 +108,9 @@ function parseJSON(str) {
|
|
|
105
108
|
return str;
|
|
106
109
|
}
|
|
107
110
|
}
|
|
111
|
+
export function getOmniSessions(parentTargetId) {
|
|
112
|
+
return omniSessions.get(parentTargetId);
|
|
113
|
+
}
|
|
108
114
|
export function cleanupOmniBroker(parentTargetId) {
|
|
109
115
|
const iframeMap = omniSessions.get(parentTargetId);
|
|
110
116
|
if (iframeMap) {
|
package/dist/cdp/profiles.d.ts
CHANGED
|
@@ -1 +1,11 @@
|
|
|
1
1
|
export declare function getSessionProfile(cookies: boolean, customProfileDir?: string, syncDataAcrossBrowsers?: boolean, browserPath?: string): string;
|
|
2
|
+
export declare function sanitizeAppName(appName: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Asserts that the given profile directory is NOT a real desktop app's native user data directory.
|
|
5
|
+
* Throws a security block error if it matches root or subdirectory of standard app data paths.
|
|
6
|
+
*/
|
|
7
|
+
export declare function assertNotRealElectronProfile(profileDir: string, appName: string): void;
|
|
8
|
+
/**
|
|
9
|
+
* Resolves the isolated profile directory for a specific Electron application.
|
|
10
|
+
*/
|
|
11
|
+
export declare function getElectronProfile(appName: string, customProfileDir?: string): string;
|
package/dist/cdp/profiles.js
CHANGED
|
@@ -85,3 +85,68 @@ export function getSessionProfile(cookies, customProfileDir, syncDataAcrossBrows
|
|
|
85
85
|
}
|
|
86
86
|
return sessionProfile;
|
|
87
87
|
}
|
|
88
|
+
// ─── Electron App Profile Resolver & Safety Guard ─────────────────────────────
|
|
89
|
+
export function sanitizeAppName(appName) {
|
|
90
|
+
const cleaned = appName.trim().replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_").replace(/^_+|_+$/g, "").toLowerCase();
|
|
91
|
+
return cleaned || "app";
|
|
92
|
+
}
|
|
93
|
+
// Real application data directories that should NEVER be used directly
|
|
94
|
+
const REAL_DESKTOP_APP_ROOTS = [];
|
|
95
|
+
if (PLATFORM === 'win32') {
|
|
96
|
+
REAL_DESKTOP_APP_ROOTS.push(path.join(HOME, "AppData", "Roaming"), path.join(HOME, "AppData", "Local"), path.join(HOME, "AppData", "LocalLow"));
|
|
97
|
+
}
|
|
98
|
+
else if (PLATFORM === 'darwin') {
|
|
99
|
+
REAL_DESKTOP_APP_ROOTS.push(path.join(HOME, "Library", "Application Support"), path.join(HOME, "Library", "Saved Application State"), path.join(HOME, "Library", "Caches"));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
REAL_DESKTOP_APP_ROOTS.push(path.join(HOME, ".config"), path.join(HOME, ".local", "share"), path.join(HOME, ".cache"));
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Asserts that the given profile directory is NOT a real desktop app's native user data directory.
|
|
106
|
+
* Throws a security block error if it matches root or subdirectory of standard app data paths.
|
|
107
|
+
*/
|
|
108
|
+
export function assertNotRealElectronProfile(profileDir, appName) {
|
|
109
|
+
const resolved = path.resolve(profileDir).toLowerCase();
|
|
110
|
+
// First check against browser profiles
|
|
111
|
+
assertNotRealBrowserProfile(resolved);
|
|
112
|
+
// Check against native desktop app data roots
|
|
113
|
+
for (const rootPath of REAL_DESKTOP_APP_ROOTS) {
|
|
114
|
+
const rootResolved = path.resolve(rootPath).toLowerCase();
|
|
115
|
+
// Direct matches or direct children in %APPDATA%/<appName>
|
|
116
|
+
const appSpecificRealDir = path.join(rootResolved, sanitizeAppName(appName)).toLowerCase();
|
|
117
|
+
if (resolved === appSpecificRealDir || resolved.startsWith(appSpecificRealDir + path.sep)) {
|
|
118
|
+
throw new Error(`[Weavetab] ✗ SECURITY BLOCK: The profileDir "${profileDir}" points to your real ${appName} desktop app data.\n` +
|
|
119
|
+
` Weavetab will never read from or write to your personal desktop app data.\n` +
|
|
120
|
+
` Please use an isolated directory or let Weavetab manage ~/.weavetab-system/apps/${appName}/.\n` +
|
|
121
|
+
` Real app data path detected: ${appSpecificRealDir}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Resolves the isolated profile directory for a specific Electron application.
|
|
127
|
+
*/
|
|
128
|
+
export function getElectronProfile(appName, customProfileDir) {
|
|
129
|
+
const sanitized = sanitizeAppName(appName);
|
|
130
|
+
if (customProfileDir && customProfileDir.trim()) {
|
|
131
|
+
const resolved = path.resolve(customProfileDir.trim());
|
|
132
|
+
assertNotRealElectronProfile(resolved, appName);
|
|
133
|
+
if (!fs.existsSync(resolved)) {
|
|
134
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
135
|
+
logDebug(`[Weavetab] ✓ Created custom Electron profile directory for ${appName}: ${resolved}`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
logDebug(`[Weavetab] ✓ Using custom Electron profile directory for ${appName}: ${resolved}`);
|
|
139
|
+
}
|
|
140
|
+
return resolved;
|
|
141
|
+
}
|
|
142
|
+
const appProfileDir = path.join(HOME, ".weavetab-system", "apps", sanitized);
|
|
143
|
+
assertNotRealElectronProfile(appProfileDir, appName);
|
|
144
|
+
if (!fs.existsSync(appProfileDir)) {
|
|
145
|
+
fs.mkdirSync(appProfileDir, { recursive: true });
|
|
146
|
+
logDebug(`[Weavetab] ✓ Created isolated Electron profile directory for ${appName}: ${appProfileDir}`);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
logDebug(`[Weavetab] ✓ Using isolated Electron profile directory for ${appName}: ${appProfileDir}`);
|
|
150
|
+
}
|
|
151
|
+
return appProfileDir;
|
|
152
|
+
}
|
package/dist/cdp/walker.js
CHANGED
|
@@ -387,6 +387,56 @@ export async function buildActionMap(session, options = {}, targetId = "default"
|
|
|
387
387
|
}
|
|
388
388
|
}
|
|
389
389
|
}
|
|
390
|
+
// Merge elements from child webviews and iframes managed by OmniBroker
|
|
391
|
+
try {
|
|
392
|
+
const { getOmniSessions } = await import("./omni_broker.js");
|
|
393
|
+
const childSessions = getOmniSessions(targetId);
|
|
394
|
+
if (childSessions && childSessions.size > 0) {
|
|
395
|
+
for (const [, childSession] of childSessions.entries()) {
|
|
396
|
+
try {
|
|
397
|
+
const { result: childResult } = await childSession.Runtime.evaluate({
|
|
398
|
+
expression: `(function() {
|
|
399
|
+
var INTERACTIVE_SEL = 'a, button, input, textarea, select, [role="button"], [role="link"], [role="textbox"], [role="checkbox"], [role="radio"], [role="combobox"], [role="searchbox"], [role="menuitem"], [role="tab"], [contenteditable="true"], [tabindex]:not([tabindex="-1"])';
|
|
400
|
+
var els = document.querySelectorAll(INTERACTIVE_SEL);
|
|
401
|
+
var results = [];
|
|
402
|
+
for (var i = 0; i < els.length; i++) {
|
|
403
|
+
var el = els[i];
|
|
404
|
+
var rect = el.getBoundingClientRect();
|
|
405
|
+
if (rect.width === 0 || rect.height === 0 || rect.bottom < 0 || rect.top > window.innerHeight) continue;
|
|
406
|
+
var id = el.getAttribute('data-wt-id');
|
|
407
|
+
if (!id) {
|
|
408
|
+
id = 'wv_' + Math.random().toString(36).slice(2, 9);
|
|
409
|
+
el.setAttribute('data-wt-id', id);
|
|
410
|
+
}
|
|
411
|
+
results.push({
|
|
412
|
+
id: id,
|
|
413
|
+
role: el.getAttribute('role') || el.tagName.toLowerCase(),
|
|
414
|
+
label: el.getAttribute('aria-label') || el.innerText || el.value || el.placeholder || '',
|
|
415
|
+
value: el.value,
|
|
416
|
+
x: Math.round(rect.left + rect.width / 2),
|
|
417
|
+
y: Math.round(rect.top + rect.height / 2),
|
|
418
|
+
selector: '[data-wt-id="' + id + '"]',
|
|
419
|
+
frameUrl: window.location.href,
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
return JSON.stringify(results);
|
|
423
|
+
})()`,
|
|
424
|
+
returnByValue: true,
|
|
425
|
+
});
|
|
426
|
+
if (childResult && childResult.value) {
|
|
427
|
+
const childEls = JSON.parse(childResult.value);
|
|
428
|
+
if (Array.isArray(childEls) && childEls.length > 0) {
|
|
429
|
+
if (!Array.isArray(extensionMap))
|
|
430
|
+
extensionMap = [];
|
|
431
|
+
extensionMap = extensionMap.concat(childEls);
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
catch { }
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
catch { }
|
|
390
440
|
if (!extensionMap || !Array.isArray(extensionMap)) {
|
|
391
441
|
return { url, title, elements: [], targetId };
|
|
392
442
|
}
|
package/dist/cli/plugin.d.ts
CHANGED
|
@@ -4,6 +4,4 @@ export declare function runPluginSearch(query?: string): Promise<void>;
|
|
|
4
4
|
export declare function runPluginBrowse(): Promise<void>;
|
|
5
5
|
export declare function runPluginRemove(pkgName: string, args?: string[]): Promise<void>;
|
|
6
6
|
export declare function runPluginVerify(pkgName: string): Promise<void>;
|
|
7
|
-
export declare function runPluginInit(args?: string[]): Promise<void>;
|
|
8
|
-
export declare function runPluginBuild(args?: string[]): Promise<void>;
|
|
9
7
|
export declare function runPluginCli(args: string[]): Promise<void>;
|