@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
package/dist/cli/updater.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function checkForUpdates(): Promise<void>;
|
|
1
|
+
export declare function checkForUpdates(force?: boolean): Promise<void>;
|
package/dist/cli/updater.js
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
3
4
|
import { fileURLToPath } from "node:url";
|
|
4
5
|
const __filename = fileURLToPath(import.meta.url);
|
|
5
6
|
const __dirname = path.dirname(__filename);
|
|
6
|
-
|
|
7
|
+
const UPDATE_CACHE_FILE = path.join(os.homedir(), ".weavetab-system", "cache", "update_check.json");
|
|
8
|
+
const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
9
|
+
export async function checkForUpdates(force = false) {
|
|
7
10
|
try {
|
|
11
|
+
const now = Date.now();
|
|
12
|
+
// Check cache unless forced
|
|
13
|
+
if (!force && fs.existsSync(UPDATE_CACHE_FILE)) {
|
|
14
|
+
try {
|
|
15
|
+
const cached = JSON.parse(fs.readFileSync(UPDATE_CACHE_FILE, "utf-8"));
|
|
16
|
+
if (cached.lastChecked && now - cached.lastChecked < CHECK_INTERVAL_MS) {
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
catch { /* malformed cache */ }
|
|
21
|
+
}
|
|
8
22
|
// Read local package.json to get current version
|
|
9
23
|
const packageJsonPath = path.resolve(__dirname, "../../package.json");
|
|
10
24
|
if (!fs.existsSync(packageJsonPath))
|
|
11
25
|
return;
|
|
12
26
|
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
13
27
|
const currentVersion = pkg.version;
|
|
14
|
-
// Fast, non-blocking fetch with
|
|
28
|
+
// Fast, non-blocking fetch with 1.5-second timeout
|
|
15
29
|
const controller = new AbortController();
|
|
16
|
-
const timeoutId = setTimeout(() => controller.abort(),
|
|
30
|
+
const timeoutId = setTimeout(() => controller.abort(), 1500);
|
|
17
31
|
const res = await fetch("https://registry.npmjs.org/@weavetab/mcp/latest", {
|
|
18
32
|
signal: controller.signal,
|
|
19
33
|
});
|
|
@@ -22,12 +36,18 @@ export async function checkForUpdates() {
|
|
|
22
36
|
return;
|
|
23
37
|
const data = await res.json();
|
|
24
38
|
const latestVersion = data.version;
|
|
25
|
-
//
|
|
39
|
+
// Save timestamp to cache
|
|
40
|
+
try {
|
|
41
|
+
fs.mkdirSync(path.dirname(UPDATE_CACHE_FILE), { recursive: true });
|
|
42
|
+
fs.writeFileSync(UPDATE_CACHE_FILE, JSON.stringify({ lastChecked: now, latestVersion }), "utf-8");
|
|
43
|
+
}
|
|
44
|
+
catch { /* non-fatal */ }
|
|
45
|
+
// Simple version comparison
|
|
26
46
|
if (isNewerVersion(currentVersion, latestVersion)) {
|
|
27
47
|
await printUpdateBox(currentVersion, latestVersion);
|
|
28
48
|
}
|
|
29
49
|
}
|
|
30
|
-
catch
|
|
50
|
+
catch {
|
|
31
51
|
// Silently fail if offline or npm is down
|
|
32
52
|
}
|
|
33
53
|
}
|
|
@@ -52,7 +72,7 @@ async function printUpdateBox(current, latest) {
|
|
|
52
72
|
chalk.bold(`Update available! `) + chalk.dim(current) + chalk.reset(` → `) + chalk.green(latest),
|
|
53
73
|
`Run ${chalk.cyan("npm i -g @weavetab/mcp")} to update.`,
|
|
54
74
|
``,
|
|
55
|
-
`Changelog: ${chalk.blue(`https://
|
|
75
|
+
`Changelog: ${chalk.blue(`https://github.com/weavetab/mcp/releases/tag/v${latest}`)}`
|
|
56
76
|
].join("\n");
|
|
57
77
|
const box = boxen(msg, {
|
|
58
78
|
padding: 1,
|
|
@@ -67,6 +87,6 @@ async function printUpdateBox(current, latest) {
|
|
|
67
87
|
// Fallback if formatting fails
|
|
68
88
|
console.error(`\n[Weavetab MCP] Update available: ${current} -> ${latest}`);
|
|
69
89
|
console.error(`Run "npm i -g @weavetab/mcp" to update.`);
|
|
70
|
-
console.error(`Changelog: https://
|
|
90
|
+
console.error(`Changelog: https://github.com/weavetab/mcp/releases/tag/v${latest}\n`);
|
|
71
91
|
}
|
|
72
92
|
}
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -6,6 +6,13 @@ export interface PolicyEntry {
|
|
|
6
6
|
pattern: string;
|
|
7
7
|
mode: PolicyMode;
|
|
8
8
|
}
|
|
9
|
+
export interface ElectronAppConfig {
|
|
10
|
+
path?: string;
|
|
11
|
+
port?: number;
|
|
12
|
+
url?: string;
|
|
13
|
+
args?: string[];
|
|
14
|
+
profileDir?: string;
|
|
15
|
+
}
|
|
9
16
|
export interface Config {
|
|
10
17
|
mode: "session" | "attach";
|
|
11
18
|
session: string;
|
|
@@ -15,13 +22,13 @@ export interface Config {
|
|
|
15
22
|
browserPath: string;
|
|
16
23
|
browserType: BrowserEngine;
|
|
17
24
|
headless: boolean;
|
|
18
|
-
|
|
19
|
-
darkMode: boolean;
|
|
20
|
-
disableExtensions: boolean;
|
|
25
|
+
extension: boolean;
|
|
21
26
|
browserFlags: string[];
|
|
22
27
|
profileDir: string;
|
|
23
28
|
cookies: boolean;
|
|
24
29
|
syncDataAcrossBrowsers: boolean;
|
|
30
|
+
apps?: Record<string, ElectronAppConfig>;
|
|
31
|
+
electron?: Record<string, ElectronAppConfig>;
|
|
25
32
|
role: Role;
|
|
26
33
|
block: string[];
|
|
27
34
|
allow: string[];
|
|
@@ -48,6 +55,11 @@ export declare const GLOBAL_CONFIG_PATH: string;
|
|
|
48
55
|
export declare const DEFAULT_CONFIG: Config;
|
|
49
56
|
export type OnConfigChange = (newConfig: Config) => void;
|
|
50
57
|
export declare function onConfigChange(cb: OnConfigChange): void;
|
|
58
|
+
/**
|
|
59
|
+
* Migrates old sectioned config to new simplified flat format.
|
|
60
|
+
*/
|
|
61
|
+
export declare function migrateToSimple(sectioned: any): Config;
|
|
62
|
+
export declare function validateConfigInternal(raw: Record<string, unknown>): Config;
|
|
51
63
|
/**
|
|
52
64
|
* Priority: Global (user) → Built-in defaults
|
|
53
65
|
*
|
|
@@ -87,9 +99,7 @@ export declare function getConfigTelemetry(config: Config): boolean;
|
|
|
87
99
|
export declare function getConfigBrowserPath(config: Config): string;
|
|
88
100
|
export declare function getConfigBrowserType(config: Config): BrowserEngine;
|
|
89
101
|
export declare function getConfigHeadless(config: Config): boolean;
|
|
90
|
-
export declare function
|
|
91
|
-
export declare function getConfigDarkMode(config: Config): boolean;
|
|
92
|
-
export declare function getConfigDisableExtensions(config: Config): boolean;
|
|
102
|
+
export declare function getConfigExtension(config: Config): boolean;
|
|
93
103
|
export declare function getConfigBrowserFlags(config: Config): string[];
|
|
94
104
|
export declare function getConfigProfileDir(config: Config): string;
|
|
95
105
|
export declare function getConfigCookies(config: Config): boolean;
|
|
@@ -108,13 +118,30 @@ export declare function getConfigActionabilityTimeoutMs(config: Config): number;
|
|
|
108
118
|
export declare function getConfigGithubCacheTtl(config: Config): number;
|
|
109
119
|
export declare function getConfigRefCacheTtl(config: Config): number;
|
|
110
120
|
export declare function getConfigDevMode(config: Config): boolean;
|
|
121
|
+
export declare function getConfigApps(config: Config): Record<string, ElectronAppConfig> | undefined;
|
|
122
|
+
export declare function getConfigElectron(config: Config): Record<string, ElectronAppConfig> | undefined;
|
|
123
|
+
export declare function getAppConfig(appName: string, config?: Config): ElectronAppConfig | null;
|
|
124
|
+
export declare function getElectronAppConfig(appName: string, config?: Config): ElectronAppConfig | null;
|
|
125
|
+
export declare function getAppNames(config?: Config): string[];
|
|
126
|
+
export declare function getElectronAppNames(config?: Config): string[];
|
|
127
|
+
export declare function getApps(config?: Config): Record<string, ElectronAppConfig>;
|
|
128
|
+
export declare function getElectronApps(config?: Config): Record<string, ElectronAppConfig>;
|
|
111
129
|
export declare function getConfigNavigationTimeoutMs(config: Config): number;
|
|
112
130
|
export declare function getConfigGithubCacheTtlMs(config: Config): number;
|
|
113
131
|
export declare function getConfigRefCacheTtlMs(config: Config): number;
|
|
114
132
|
export declare function getSystemDir(subdir: string): string;
|
|
133
|
+
export declare function getSystemAppDir(appName: string): string;
|
|
115
134
|
/**
|
|
116
135
|
* Returns a directory inside the global ~/.weavetab-system/ folder.
|
|
117
136
|
* Alias for consistency — identical to getSystemDir.
|
|
118
137
|
*/
|
|
119
138
|
export declare function getGlobalSystemDir(subdir: string): string;
|
|
139
|
+
/**
|
|
140
|
+
* Returns the canonical directory for persistent site profiles and learned workflow patterns.
|
|
141
|
+
* Lives in ~/.weavetab-system/memory/sites/ — strictly machine-managed, never user-authored.
|
|
142
|
+
* Separated from the legacy selector-memory dir (~/.weavetab-system/memory/) so existing
|
|
143
|
+
* per-domain selector caches are not disrupted.
|
|
144
|
+
*/
|
|
145
|
+
export declare function getSystemMemorySitesDir(): string;
|
|
120
146
|
export declare function saveConfig(config: Config): void;
|
|
147
|
+
export declare function stopConfigWatcher(): void;
|
package/dist/config/loader.js
CHANGED
|
@@ -30,13 +30,13 @@ export const DEFAULT_CONFIG = {
|
|
|
30
30
|
browserPath: "auto",
|
|
31
31
|
browserType: "chromium",
|
|
32
32
|
headless: false,
|
|
33
|
-
|
|
34
|
-
darkMode: true,
|
|
35
|
-
disableExtensions: false,
|
|
33
|
+
extension: true,
|
|
36
34
|
browserFlags: [],
|
|
37
35
|
profileDir: "",
|
|
38
36
|
cookies: false,
|
|
39
37
|
syncDataAcrossBrowsers: false,
|
|
38
|
+
apps: undefined,
|
|
39
|
+
electron: undefined,
|
|
40
40
|
role: "admin",
|
|
41
41
|
block: ["*.bank.*", "paypal.com"],
|
|
42
42
|
allow: [],
|
|
@@ -44,7 +44,7 @@ export const DEFAULT_CONFIG = {
|
|
|
44
44
|
sessionTimeout: 3600,
|
|
45
45
|
navigationTimeout: 30000,
|
|
46
46
|
idleTimeout: 60000,
|
|
47
|
-
actionabilityTimeoutMs:
|
|
47
|
+
actionabilityTimeoutMs: 300,
|
|
48
48
|
maxTabs: 10,
|
|
49
49
|
maxActionsPerMinute: 60,
|
|
50
50
|
githubCacheTtlMs: 30 * 60 * 1000,
|
|
@@ -77,7 +77,7 @@ function isSectionedConfig(raw) {
|
|
|
77
77
|
/**
|
|
78
78
|
* Migrates old sectioned config to new simplified flat format.
|
|
79
79
|
*/
|
|
80
|
-
function migrateToSimple(sectioned) {
|
|
80
|
+
export function migrateToSimple(sectioned) {
|
|
81
81
|
return {
|
|
82
82
|
mode: sectioned.general?.mode || "session",
|
|
83
83
|
session: sectioned.general?.session || "default",
|
|
@@ -87,12 +87,12 @@ function migrateToSimple(sectioned) {
|
|
|
87
87
|
browserPath: sectioned.browser?.path || "auto",
|
|
88
88
|
browserType: sectioned.browser?.type || "chromium",
|
|
89
89
|
headless: sectioned.browser?.headless ?? false,
|
|
90
|
-
|
|
91
|
-
darkMode: sectioned.browser?.darkMode ?? true,
|
|
92
|
-
disableExtensions: sectioned.browser?.disableExtensions || false,
|
|
90
|
+
extension: sectioned.browser?.extension ?? sectioned.browser?.visuals ?? true,
|
|
93
91
|
browserFlags: sectioned.browser?.flags || [],
|
|
94
92
|
profileDir: sectioned.browser?.profileDir || "",
|
|
95
93
|
cookies: sectioned.browser?.cookies || false,
|
|
94
|
+
apps: sectioned.apps || sectioned.electron || undefined,
|
|
95
|
+
electron: sectioned.apps || sectioned.electron || undefined,
|
|
96
96
|
role: sectioned.security?.role || "admin",
|
|
97
97
|
block: sectioned.security?.block || ["*.bank.*", "paypal.com"],
|
|
98
98
|
allow: sectioned.security?.allow || [],
|
|
@@ -149,7 +149,7 @@ function validateConfig(raw) {
|
|
|
149
149
|
// Validate simplified config
|
|
150
150
|
return validateConfigInternal(raw);
|
|
151
151
|
}
|
|
152
|
-
function validateConfigInternal(raw) {
|
|
152
|
+
export function validateConfigInternal(raw) {
|
|
153
153
|
const d = DEFAULT_CONFIG;
|
|
154
154
|
const VALID_MODES = ["session", "attach"];
|
|
155
155
|
const VALID_ENGINES = ["chromium", "firefox", "webkit"];
|
|
@@ -205,6 +205,83 @@ function validateConfigInternal(raw) {
|
|
|
205
205
|
logDebug(`[Weavetab Config] [WARN] Proxy contains literal credentials (://user:pass@). This is a security risk. Use an environment variable instead. Ignoring proxy.`);
|
|
206
206
|
proxy = undefined;
|
|
207
207
|
}
|
|
208
|
+
// Validate apps / electron apps mapping
|
|
209
|
+
let electron = undefined;
|
|
210
|
+
const rawApps = raw.apps || raw.electron;
|
|
211
|
+
if (rawApps && typeof rawApps === "object" && !Array.isArray(rawApps)) {
|
|
212
|
+
const rawMap = rawApps;
|
|
213
|
+
const validatedMap = {};
|
|
214
|
+
for (const [rawKey, rawVal] of Object.entries(rawMap)) {
|
|
215
|
+
const sanitizedKey = rawKey.trim();
|
|
216
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(sanitizedKey)) {
|
|
217
|
+
logDebug(`[Weavetab Config] [WARN] Invalid app name "${rawKey}". Must be alphanumeric, dashes, or underscores.`);
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (typeof rawVal === "string") {
|
|
221
|
+
const strVal = rawVal.trim();
|
|
222
|
+
if (/^\d+$/.test(strVal)) {
|
|
223
|
+
const portNum = parseInt(strVal, 10);
|
|
224
|
+
if (portNum > 0 && portNum <= 65535) {
|
|
225
|
+
validatedMap[sanitizedKey] = { port: portNum };
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
else if (strVal.startsWith("http://") || strVal.startsWith("https://") || strVal.startsWith("ws://") || strVal.startsWith("wss://")) {
|
|
229
|
+
const cfg = { url: strVal };
|
|
230
|
+
try {
|
|
231
|
+
const parsedUrl = new URL(strVal);
|
|
232
|
+
if (parsedUrl.port) {
|
|
233
|
+
const p = parseInt(parsedUrl.port, 10);
|
|
234
|
+
if (p > 0 && p <= 65535)
|
|
235
|
+
cfg.port = p;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch { /* ignore */ }
|
|
239
|
+
validatedMap[sanitizedKey] = cfg;
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
const safePath = safePathField(`apps.${sanitizedKey}`, strVal);
|
|
243
|
+
if (safePath) {
|
|
244
|
+
validatedMap[sanitizedKey] = { path: safePath };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (typeof rawVal === "number" && Number.isFinite(rawVal) && rawVal > 0 && rawVal <= 65535) {
|
|
249
|
+
validatedMap[sanitizedKey] = { port: rawVal };
|
|
250
|
+
}
|
|
251
|
+
else if (typeof rawVal === "object" && rawVal !== null && !Array.isArray(rawVal)) {
|
|
252
|
+
const objVal = rawVal;
|
|
253
|
+
const appCfg = {};
|
|
254
|
+
if (typeof objVal.path === "string" && objVal.path.trim()) {
|
|
255
|
+
const safePath = safePathField(`apps.${sanitizedKey}.path`, objVal.path);
|
|
256
|
+
if (safePath)
|
|
257
|
+
appCfg.path = safePath;
|
|
258
|
+
}
|
|
259
|
+
if (typeof objVal.port === "number" && Number.isFinite(objVal.port) && objVal.port > 0 && objVal.port <= 65535) {
|
|
260
|
+
appCfg.port = objVal.port;
|
|
261
|
+
}
|
|
262
|
+
else if (typeof objVal.port === "string" && /^\d+$/.test(objVal.port.trim())) {
|
|
263
|
+
const p = parseInt(objVal.port.trim(), 10);
|
|
264
|
+
if (p > 0 && p <= 65535)
|
|
265
|
+
appCfg.port = p;
|
|
266
|
+
}
|
|
267
|
+
if (typeof objVal.url === "string" && objVal.url.trim()) {
|
|
268
|
+
appCfg.url = objVal.url.trim();
|
|
269
|
+
}
|
|
270
|
+
if (Array.isArray(objVal.args)) {
|
|
271
|
+
appCfg.args = objVal.args.filter(isStr);
|
|
272
|
+
}
|
|
273
|
+
if (typeof objVal.profileDir === "string" && objVal.profileDir.trim()) {
|
|
274
|
+
appCfg.profileDir = safePathField(`apps.${sanitizedKey}.profileDir`, objVal.profileDir);
|
|
275
|
+
}
|
|
276
|
+
if (appCfg.path || appCfg.port || appCfg.url) {
|
|
277
|
+
validatedMap[sanitizedKey] = appCfg;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (Object.keys(validatedMap).length > 0) {
|
|
282
|
+
electron = validatedMap;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
208
285
|
return {
|
|
209
286
|
mode,
|
|
210
287
|
session: validateField("session", raw.session, isStr, d.session),
|
|
@@ -214,12 +291,12 @@ function validateConfigInternal(raw) {
|
|
|
214
291
|
browserPath,
|
|
215
292
|
browserType: validateField("browserType", raw.browserType, isStr, d.browserType, v => VALID_ENGINES.includes(v)),
|
|
216
293
|
headless: validateField("headless", raw.headless, isBool, d.headless),
|
|
217
|
-
|
|
218
|
-
darkMode: validateField("darkMode", raw.darkMode, isBool, d.darkMode),
|
|
219
|
-
disableExtensions: validateField("disableExtensions", raw.disableExtensions, isBool, d.disableExtensions),
|
|
294
|
+
extension: validateField("extension", raw.extension, isBool, d.extension),
|
|
220
295
|
browserFlags: isArr(raw.browserFlags) ? raw.browserFlags.filter(isStr) : d.browserFlags,
|
|
221
296
|
profileDir,
|
|
222
297
|
cookies: validateField("cookies", raw.cookies, isBool, d.cookies),
|
|
298
|
+
apps: electron,
|
|
299
|
+
electron,
|
|
223
300
|
role: validateField("role", raw.role, isStr, d.role, v => VALID_ROLES.includes(v)),
|
|
224
301
|
block: isArr(raw.block) ? raw.block.filter(isStr) : d.block,
|
|
225
302
|
allow: isArr(raw.allow) ? raw.allow.filter(isStr) : d.allow,
|
|
@@ -345,7 +422,7 @@ function resolveConfigMerge() {
|
|
|
345
422
|
logDebug(`[Weavetab Config] [OK] Active directory: ${activeDir}`);
|
|
346
423
|
}
|
|
347
424
|
else {
|
|
348
|
-
logDebug(`[Weavetab Config] [WARN] No config found — using built-in defaults. Run:
|
|
425
|
+
logDebug(`[Weavetab Config] [WARN] No config found — using built-in defaults. Run: weavetab config`);
|
|
349
426
|
logDebug(`[Weavetab Config] [OK] Active directory: ${activeDir}`);
|
|
350
427
|
// Write a starter global config so user isn't surprised
|
|
351
428
|
ensureConfigDir(globalDir);
|
|
@@ -551,14 +628,8 @@ export function getConfigBrowserType(config) {
|
|
|
551
628
|
export function getConfigHeadless(config) {
|
|
552
629
|
return config.headless;
|
|
553
630
|
}
|
|
554
|
-
export function
|
|
555
|
-
return config.
|
|
556
|
-
}
|
|
557
|
-
export function getConfigDarkMode(config) {
|
|
558
|
-
return config.darkMode;
|
|
559
|
-
}
|
|
560
|
-
export function getConfigDisableExtensions(config) {
|
|
561
|
-
return config.disableExtensions;
|
|
631
|
+
export function getConfigExtension(config) {
|
|
632
|
+
return config.extension !== false;
|
|
562
633
|
}
|
|
563
634
|
export function getConfigBrowserFlags(config) {
|
|
564
635
|
return config.browserFlags;
|
|
@@ -614,6 +685,40 @@ export function getConfigRefCacheTtl(config) {
|
|
|
614
685
|
export function getConfigDevMode(config) {
|
|
615
686
|
return config.devMode;
|
|
616
687
|
}
|
|
688
|
+
export function getConfigApps(config) {
|
|
689
|
+
return config.apps || config.electron;
|
|
690
|
+
}
|
|
691
|
+
export function getConfigElectron(config) {
|
|
692
|
+
return config.apps || config.electron;
|
|
693
|
+
}
|
|
694
|
+
export function getAppConfig(appName, config) {
|
|
695
|
+
const cfg = config ?? getCachedConfig();
|
|
696
|
+
const map = cfg.apps || cfg.electron;
|
|
697
|
+
if (!map)
|
|
698
|
+
return null;
|
|
699
|
+
const sanitized = appName.trim();
|
|
700
|
+
return map[sanitized] || null;
|
|
701
|
+
}
|
|
702
|
+
export function getElectronAppConfig(appName, config) {
|
|
703
|
+
return getAppConfig(appName, config);
|
|
704
|
+
}
|
|
705
|
+
export function getAppNames(config) {
|
|
706
|
+
const cfg = config ?? getCachedConfig();
|
|
707
|
+
const map = cfg.apps || cfg.electron;
|
|
708
|
+
if (!map)
|
|
709
|
+
return [];
|
|
710
|
+
return Object.keys(map);
|
|
711
|
+
}
|
|
712
|
+
export function getElectronAppNames(config) {
|
|
713
|
+
return getAppNames(config);
|
|
714
|
+
}
|
|
715
|
+
export function getApps(config) {
|
|
716
|
+
const cfg = config ?? getCachedConfig();
|
|
717
|
+
return cfg.apps || cfg.electron || {};
|
|
718
|
+
}
|
|
719
|
+
export function getElectronApps(config) {
|
|
720
|
+
return getApps(config);
|
|
721
|
+
}
|
|
617
722
|
// ─── Additional Helper Functions for Code Compatibility ─────────────────────────
|
|
618
723
|
export function getConfigNavigationTimeoutMs(config) {
|
|
619
724
|
return config.navigationTimeout;
|
|
@@ -637,6 +742,14 @@ export function getSystemDir(subdir) {
|
|
|
637
742
|
}
|
|
638
743
|
return dir;
|
|
639
744
|
}
|
|
745
|
+
export function getSystemAppDir(appName) {
|
|
746
|
+
const sanitized = appName.trim().replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
747
|
+
const dir = path.join(GLOBAL_SYSTEM_DIR, "apps", sanitized);
|
|
748
|
+
if (!fs.existsSync(dir)) {
|
|
749
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
750
|
+
}
|
|
751
|
+
return dir;
|
|
752
|
+
}
|
|
640
753
|
/**
|
|
641
754
|
* Returns a directory inside the global ~/.weavetab-system/ folder.
|
|
642
755
|
* Alias for consistency — identical to getSystemDir.
|
|
@@ -648,6 +761,19 @@ export function getGlobalSystemDir(subdir) {
|
|
|
648
761
|
}
|
|
649
762
|
return dir;
|
|
650
763
|
}
|
|
764
|
+
/**
|
|
765
|
+
* Returns the canonical directory for persistent site profiles and learned workflow patterns.
|
|
766
|
+
* Lives in ~/.weavetab-system/memory/sites/ — strictly machine-managed, never user-authored.
|
|
767
|
+
* Separated from the legacy selector-memory dir (~/.weavetab-system/memory/) so existing
|
|
768
|
+
* per-domain selector caches are not disrupted.
|
|
769
|
+
*/
|
|
770
|
+
export function getSystemMemorySitesDir() {
|
|
771
|
+
const dir = path.join(GLOBAL_SYSTEM_DIR, "memory", "sites");
|
|
772
|
+
if (!fs.existsSync(dir)) {
|
|
773
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
774
|
+
}
|
|
775
|
+
return dir;
|
|
776
|
+
}
|
|
651
777
|
export function saveConfig(config) {
|
|
652
778
|
const { dir, file } = getActiveConfigPath();
|
|
653
779
|
ensureConfigDir(dir);
|
|
@@ -676,11 +802,18 @@ export function saveConfig(config) {
|
|
|
676
802
|
}
|
|
677
803
|
// ─── Live-reload watcher ───────────────────────────────────────────────────────
|
|
678
804
|
let watchTimeout = null;
|
|
805
|
+
let _configWatcher = null;
|
|
679
806
|
function startWatcher(dir) {
|
|
680
807
|
try {
|
|
681
808
|
if (!fs.existsSync(dir))
|
|
682
809
|
return;
|
|
683
|
-
|
|
810
|
+
if (_configWatcher) {
|
|
811
|
+
try {
|
|
812
|
+
_configWatcher.close();
|
|
813
|
+
}
|
|
814
|
+
catch { /* ignore */ }
|
|
815
|
+
}
|
|
816
|
+
_configWatcher = fs.watch(dir, (eventType, filename) => {
|
|
684
817
|
if (filename === "config.json" && eventType === "change") {
|
|
685
818
|
if (watchTimeout)
|
|
686
819
|
clearTimeout(watchTimeout);
|
|
@@ -693,12 +826,31 @@ function startWatcher(dir) {
|
|
|
693
826
|
logDebug("[Weavetab] Failed to reload config", e);
|
|
694
827
|
}
|
|
695
828
|
}, 150);
|
|
829
|
+
if (watchTimeout && typeof watchTimeout.unref === "function") {
|
|
830
|
+
watchTimeout.unref();
|
|
831
|
+
}
|
|
696
832
|
}
|
|
697
833
|
});
|
|
834
|
+
if (_configWatcher && typeof _configWatcher.unref === "function") {
|
|
835
|
+
_configWatcher.unref();
|
|
836
|
+
}
|
|
698
837
|
}
|
|
699
838
|
catch {
|
|
700
839
|
// Ignore watch errors on some OS/network mounts
|
|
701
840
|
}
|
|
702
841
|
}
|
|
842
|
+
export function stopConfigWatcher() {
|
|
843
|
+
if (watchTimeout) {
|
|
844
|
+
clearTimeout(watchTimeout);
|
|
845
|
+
watchTimeout = null;
|
|
846
|
+
}
|
|
847
|
+
if (_configWatcher) {
|
|
848
|
+
try {
|
|
849
|
+
_configWatcher.close();
|
|
850
|
+
}
|
|
851
|
+
catch { /* ignore */ }
|
|
852
|
+
_configWatcher = null;
|
|
853
|
+
}
|
|
854
|
+
}
|
|
703
855
|
// Watch global location so changes are picked up
|
|
704
856
|
startWatcher(GLOBAL_CONFIG_DIR);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — desktop/discovery.ts
|
|
3
|
+
* Auto-discovery scanner for running Electron desktop applications.
|
|
4
|
+
*
|
|
5
|
+
* Detects:
|
|
6
|
+
* 1. Active Electron instances with open CDP remote debugging ports
|
|
7
|
+
* (Slack, Discord, VS Code, Notion, Obsidian, Linear, Cursor, etc.).
|
|
8
|
+
* 2. Running Electron processes without remote debugging enabled,
|
|
9
|
+
* providing actionable launch flags.
|
|
10
|
+
*
|
|
11
|
+
* Weavetab by fy2ne
|
|
12
|
+
*/
|
|
13
|
+
export interface DiscoveredElectronApp {
|
|
14
|
+
name: string;
|
|
15
|
+
port?: number;
|
|
16
|
+
status: "debug_port_open" | "process_running_no_debug_port";
|
|
17
|
+
title?: string;
|
|
18
|
+
webSocketDebuggerUrl?: string;
|
|
19
|
+
browserVersion?: string;
|
|
20
|
+
pid?: number;
|
|
21
|
+
recommendation?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare const COMMON_DEBUG_PORTS: number[];
|
|
24
|
+
/**
|
|
25
|
+
* Fast probe of a potential CDP debug port on 127.0.0.1.
|
|
26
|
+
* Resolves with version/target metadata if open, or null if unreachable.
|
|
27
|
+
*/
|
|
28
|
+
export declare function probeDebugPort(port: number, timeoutMs?: number): Promise<{
|
|
29
|
+
versionInfo: any;
|
|
30
|
+
targets: any[];
|
|
31
|
+
} | null>;
|
|
32
|
+
export declare function fetchTargets(port: number, timeoutMs?: number): Promise<any[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Infers an application name from CDP version and target metadata.
|
|
35
|
+
*/
|
|
36
|
+
export declare function identifyElectronAppName(versionInfo: any, targets: any[]): string;
|
|
37
|
+
/**
|
|
38
|
+
* Discovers running Electron apps on the host machine.
|
|
39
|
+
* 1. Probes common and provided debug ports for open CDP listeners.
|
|
40
|
+
* 2. Checks OS process list for running Electron apps lacking debug ports.
|
|
41
|
+
*/
|
|
42
|
+
export declare function discoverElectronApps(customPorts?: number[]): Promise<DiscoveredElectronApp[]>;
|
|
43
|
+
export declare function isValidElectronExecutablePath(execPath: string): boolean;
|
|
44
|
+
export declare function resolveElectronExecutablePath(appName: string, configuredPath?: string): string | null;
|