micro-models-agent 0.41.2 → 0.43.0
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/bin/mma.mjs +41 -41
- package/dist/cli/commands.js +19 -9
- package/dist/cli/completer.js +37 -36
- package/dist/cli/index.js +2 -2
- package/dist/cli/main.js +23 -48
- package/dist/cli/repl-commands.js +12 -40
- package/dist/cli/repl.js +87 -217
- package/dist/cli/security-commands.js +7 -5
- package/dist/cli/setup.js +26 -8
- package/dist/config/config.js +5 -52
- package/dist/config/defaults.js +5 -29
- package/dist/config/experts.js +1 -1
- package/dist/config/index.js +3 -3
- package/dist/config/security.js +10 -3
- package/dist/core/agent-moe.js +10 -2
- package/dist/core/agent.js +82 -273
- package/dist/core/bootstrap.js +13 -80
- package/dist/core/index.js +2 -2
- package/dist/core/prompt-builder.js +2 -23
- package/dist/core/session-logger.js +4 -46
- package/dist/i18n/en.json +2 -75
- package/dist/i18n/ru.json +1 -74
- package/dist/index.js +1 -1
- package/dist/llm/image-utils.js +5 -4
- package/dist/llm/index.js +4 -4
- package/dist/llm/model-loader.js +6 -6
- package/dist/llm/openai-compat.js +34 -40
- package/dist/llm/orchestrator.js +29 -33
- package/dist/llm/response.js +9 -9
- package/dist/logger/app-logger.js +1 -1
- package/dist/logger/index.js +1 -1
- package/dist/main.js +648 -183
- package/dist/migration/backup.js +13 -13
- package/dist/migration/detect.js +11 -11
- package/dist/migration/index.js +2 -2
- package/dist/modules/browser/actions.js +4 -34
- package/dist/modules/browser/bridge-server.mjs +202 -202
- package/dist/modules/browser/cookie-store.js +6 -6
- package/dist/modules/browser/index.js +5 -7
- package/dist/modules/browser/module.js +7 -8
- package/dist/modules/browser/session.js +84 -87
- package/dist/modules/browser/snapshot.js +58 -92
- package/dist/modules/browser/types.js +1 -4
- package/dist/modules/certification/cli.js +4 -2
- package/dist/modules/certification/fact-checker.js +3 -1
- package/dist/modules/certification/loader.js +9 -3
- package/dist/modules/certification/runner.js +4 -1
- package/dist/modules/context/index.js +1 -1
- package/dist/modules/context/manager.js +86 -160
- package/dist/modules/execution/auditor.js +25 -177
- package/dist/modules/execution/module.js +544 -201
- package/dist/modules/execution/moe-executor.js +0 -25
- package/dist/modules/execution/plan-store.js +3 -1
- package/dist/modules/execution/plan-validator.js +10 -10
- package/dist/modules/execution/planner.js +1 -6
- package/dist/modules/execution/stuck-detector.js +10 -173
- package/dist/modules/execution/verifier.js +42 -86
- package/dist/modules/hallucination/confidence.js +1 -8
- package/dist/modules/hallucination/detector.js +5 -2
- package/dist/modules/hallucination/factual.js +64 -3
- package/dist/modules/hallucination/index.js +1 -1
- package/dist/modules/hallucination/js-identifiers.js +0 -190
- package/dist/modules/hallucination/llm-judge.js +3 -1
- package/dist/modules/indexer/cache.js +7 -9
- package/dist/modules/indexer/index.js +3 -3
- package/dist/modules/indexer/module.js +42 -95
- package/dist/modules/indexer/walker.js +17 -17
- package/dist/modules/lsp/client.js +31 -74
- package/dist/modules/lsp/config.js +33 -87
- package/dist/modules/lsp/index.js +3 -3
- package/dist/modules/lsp/module.js +21 -185
- package/dist/modules/mcp/module.js +6 -2
- package/dist/modules/memory/index.js +1 -1
- package/dist/modules/memory/module.js +23 -71
- package/dist/modules/memory/search.js +9 -11
- package/dist/modules/memory/store.js +13 -13
- package/dist/modules/pipelines/engine.js +10 -10
- package/dist/modules/pipelines/index.js +3 -3
- package/dist/modules/pipelines/parser.js +14 -17
- package/dist/modules/pipelines/template.js +1 -1
- package/dist/modules/plugins/builtin/lint-on-write.js +16 -21
- package/dist/modules/plugins/builtin/notify.js +2 -3
- package/dist/modules/plugins/index.js +1 -1
- package/dist/modules/plugins/loader.js +17 -59
- package/dist/modules/plugins/manager.js +17 -73
- package/dist/modules/processes/index.js +1 -1
- package/dist/modules/processes/registry.js +46 -135
- package/dist/modules/registry.js +2 -4
- package/dist/modules/security/audit-notifier.js +39 -39
- package/dist/modules/security/command-validator.js +8 -2
- package/dist/modules/security/data-sanitizer.js +9 -1
- package/dist/modules/security/encryption.js +56 -58
- package/dist/modules/security/network-validator.js +9 -1
- package/dist/modules/security/path-validator.js +3 -1
- package/dist/modules/security/security-policies.js +19 -3
- package/dist/modules/security/session-encryption.js +1 -1
- package/dist/modules/security/session-isolation.js +8 -8
- package/dist/modules/session/index.js +3 -3
- package/dist/modules/session/module.js +5 -5
- package/dist/modules/session/store.js +9 -3
- package/dist/modules/skills/module.js +2 -1
- package/dist/modules/updater/checker.js +6 -70
- package/dist/modules/updater/index.js +1 -2
- package/dist/modules/user-profile/compressor.js +2 -2
- package/dist/modules/user-profile/index.js +1 -1
- package/dist/modules/user-profile/profile.js +9 -9
- package/dist/tools/attach-image.js +1 -1
- package/dist/tools/bash.js +19 -178
- package/dist/tools/browser.js +29 -46
- package/dist/tools/executor.js +5 -4
- package/dist/tools/file-info.js +12 -13
- package/dist/tools/filter-tools.js +2 -9
- package/dist/tools/glob-tool.js +11 -11
- package/dist/tools/grep-tool.js +3 -1
- package/dist/tools/index.js +2 -13
- package/dist/tools/list-dir.js +17 -18
- package/dist/tools/load-skill.js +3 -1
- package/dist/tools/path-utils.js +4 -4
- package/dist/tools/pipeline-run.js +25 -25
- package/dist/tools/process-kill.js +11 -11
- package/dist/tools/process-list.js +22 -20
- package/dist/tools/process-log.js +18 -22
- package/dist/tools/question.js +3 -1
- package/dist/tools/read-file.js +2 -10
- package/dist/tools/recall.js +37 -44
- package/dist/tools/registry.js +4 -15
- package/dist/tools/remember.js +29 -29
- package/dist/tools/scope-check.js +9 -9
- package/dist/tools/subagent.js +9 -54
- package/dist/tools/user-input.js +1 -1
- package/dist/tools/web-browse.js +3 -3
- package/dist/tools/web-fetch.js +3 -3
- package/dist/tools/web-search.js +3 -3
- package/dist/tools/write-file.js +3 -1
- package/dist/ui/box.js +5 -1
- package/dist/ui/index.js +6 -6
- package/dist/ui/md-formatter.js +33 -33
- package/dist/ui/output.js +5 -5
- package/dist/ui/renderer.js +10 -15
- package/dist/ui/table.js +1 -1
- package/package.json +48 -48
- package/dist/cli/plugin-commands.js +0 -36
- package/dist/cli/run-result.js +0 -22
- package/dist/core/version.js +0 -24
- package/dist/modules/artifacts/store.js +0 -61
- package/dist/modules/browser/bridge-client.js +0 -199
- package/dist/modules/browser/bridge-path.js +0 -10
- package/dist/modules/browser/driver.js +0 -136
- package/dist/modules/context/chunk-query.js +0 -100
- package/dist/modules/context/fact-extractor.js +0 -162
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/execution/audit-runners.js +0 -152
- package/dist/modules/execution/execution-plugin.js +0 -272
- package/dist/modules/execution/plan-tool.js +0 -508
- package/dist/modules/execution/windows-commands.js +0 -41
- package/dist/modules/indexer/project-profile.js +0 -183
- package/dist/modules/lsp/check-tool.js +0 -58
- package/dist/modules/lsp/command.js +0 -60
- package/dist/modules/lsp/probe.js +0 -76
- package/dist/modules/lsp/project-root.js +0 -32
- package/dist/modules/lsp/startup-check.js +0 -141
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/updater/module.js +0 -116
- package/dist/tools/chunk-query.js +0 -99
- package/dist/tools/download-file.js +0 -116
- package/dist/tools/enable-tools.js +0 -58
- package/dist/tools/hidden-tools-block.js +0 -37
- package/dist/ui/line-editor.js +0 -703
- package/dist/ui/line-math.js +0 -69
- package/dist/ui/plan-view.js +0 -103
package/dist/migration/backup.js
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, writeFileSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { t } from
|
|
1
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, writeFileSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { t } from '../i18n/index';
|
|
4
4
|
export class BackupManager {
|
|
5
5
|
configDir;
|
|
6
6
|
constructor(configDir) {
|
|
7
7
|
this.configDir = configDir;
|
|
8
8
|
}
|
|
9
9
|
backupConfig() {
|
|
10
|
-
const configPath = join(this.configDir,
|
|
10
|
+
const configPath = join(this.configDir, 'config.json');
|
|
11
11
|
if (!existsSync(configPath))
|
|
12
12
|
return;
|
|
13
|
-
const bakPath = join(this.configDir,
|
|
13
|
+
const bakPath = join(this.configDir, 'config.json.bak');
|
|
14
14
|
copyFileSync(configPath, bakPath);
|
|
15
15
|
}
|
|
16
16
|
backupAll() {
|
|
17
|
-
const bakDir = this.configDir +
|
|
17
|
+
const bakDir = this.configDir + '.bak';
|
|
18
18
|
if (existsSync(bakDir))
|
|
19
19
|
return;
|
|
20
20
|
if (existsSync(this.configDir)) {
|
|
@@ -25,21 +25,21 @@ export class BackupManager {
|
|
|
25
25
|
if (!existsSync(this.configDir)) {
|
|
26
26
|
mkdirSync(this.configDir, { recursive: true });
|
|
27
27
|
}
|
|
28
|
-
writeFileSync(join(this.configDir,
|
|
28
|
+
writeFileSync(join(this.configDir, 'config.json'), JSON.stringify(config, null, 2), 'utf-8');
|
|
29
29
|
}
|
|
30
30
|
getBackupSummary() {
|
|
31
31
|
const lines = [];
|
|
32
|
-
const bakConfig = join(this.configDir,
|
|
33
|
-
const bakDir = this.configDir +
|
|
32
|
+
const bakConfig = join(this.configDir, 'config.json.bak');
|
|
33
|
+
const bakDir = this.configDir + '.bak';
|
|
34
34
|
if (existsSync(bakConfig)) {
|
|
35
|
-
lines.push(t(
|
|
35
|
+
lines.push(t('migration.config_bak'));
|
|
36
36
|
}
|
|
37
37
|
if (existsSync(bakDir)) {
|
|
38
38
|
const files = readdirSync(bakDir);
|
|
39
|
-
lines.push(t(
|
|
39
|
+
lines.push(t('migration.dir_bak', { count: files.length }));
|
|
40
40
|
}
|
|
41
41
|
return lines.length > 0
|
|
42
|
-
? `${t(
|
|
43
|
-
: t(
|
|
42
|
+
? `${t('migration.migrated')}\n${lines.join('\n')}`
|
|
43
|
+
: t('migration.not_needed');
|
|
44
44
|
}
|
|
45
45
|
}
|
package/dist/migration/detect.js
CHANGED
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
const V1_ONLY_FIELDS = [
|
|
1
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
const V1_ONLY_FIELDS = ['apiBase', 'apiKey', 'chatEndpoint', 'temperature', 'maxTokens'];
|
|
4
4
|
export class MigrationDetector {
|
|
5
5
|
configDir;
|
|
6
6
|
constructor(configDir) {
|
|
7
7
|
this.configDir = configDir;
|
|
8
8
|
}
|
|
9
9
|
needsMigration() {
|
|
10
|
-
const configPath = join(this.configDir,
|
|
10
|
+
const configPath = join(this.configDir, 'config.json');
|
|
11
11
|
if (!existsSync(configPath))
|
|
12
12
|
return false;
|
|
13
13
|
try {
|
|
14
|
-
const content = readFileSync(configPath,
|
|
14
|
+
const content = readFileSync(configPath, 'utf-8');
|
|
15
15
|
const config = JSON.parse(content);
|
|
16
|
-
if (config.version && config.version.startsWith(
|
|
16
|
+
if (config.version && config.version.startsWith('2.'))
|
|
17
17
|
return false;
|
|
18
18
|
for (const field of V1_ONLY_FIELDS) {
|
|
19
19
|
if (field in config)
|
|
20
20
|
return true;
|
|
21
21
|
}
|
|
22
|
-
const v2Required = [
|
|
23
|
-
const hasAllV2Fields = v2Required.every(
|
|
22
|
+
const v2Required = ['provider', 'contextWindow', 'contextBudget'];
|
|
23
|
+
const hasAllV2Fields = v2Required.every(f => f in config);
|
|
24
24
|
if (!hasAllV2Fields)
|
|
25
25
|
return true;
|
|
26
26
|
return false;
|
|
@@ -30,17 +30,17 @@ export class MigrationDetector {
|
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
hasOldSessions() {
|
|
33
|
-
const sessionsDir = join(this.configDir,
|
|
33
|
+
const sessionsDir = join(this.configDir, 'sessions');
|
|
34
34
|
if (!existsSync(sessionsDir))
|
|
35
35
|
return false;
|
|
36
36
|
return readdirSync(sessionsDir).length > 0;
|
|
37
37
|
}
|
|
38
38
|
hasOldProfile() {
|
|
39
|
-
const profilePath = join(this.configDir,
|
|
39
|
+
const profilePath = join(this.configDir, 'profile.json');
|
|
40
40
|
if (!existsSync(profilePath))
|
|
41
41
|
return false;
|
|
42
42
|
try {
|
|
43
|
-
const profile = JSON.parse(readFileSync(profilePath,
|
|
43
|
+
const profile = JSON.parse(readFileSync(profilePath, 'utf-8'));
|
|
44
44
|
return !profile.platform && !profile.os;
|
|
45
45
|
}
|
|
46
46
|
catch {
|
package/dist/migration/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { MigrationDetector } from
|
|
2
|
-
export { BackupManager } from
|
|
1
|
+
export { MigrationDetector } from './detect';
|
|
2
|
+
export { BackupManager } from './backup';
|
|
@@ -19,13 +19,13 @@ export function buildTypeScript(elementIndex, text) {
|
|
|
19
19
|
}
|
|
20
20
|
export function buildScrollScript(direction) {
|
|
21
21
|
switch (direction) {
|
|
22
|
-
case
|
|
22
|
+
case 'down':
|
|
23
23
|
return `window.scrollBy({ top: 600, behavior: 'smooth' })`;
|
|
24
|
-
case
|
|
24
|
+
case 'up':
|
|
25
25
|
return `window.scrollBy({ top: -600, behavior: 'smooth' })`;
|
|
26
|
-
case
|
|
26
|
+
case 'top':
|
|
27
27
|
return `window.scrollTo({ top: 0, behavior: 'smooth' })`;
|
|
28
|
-
case
|
|
28
|
+
case 'bottom':
|
|
29
29
|
return `window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' })`;
|
|
30
30
|
}
|
|
31
31
|
}
|
|
@@ -44,33 +44,3 @@ export function buildIndexInjectionScript() {
|
|
|
44
44
|
});
|
|
45
45
|
`;
|
|
46
46
|
}
|
|
47
|
-
export function buildTextExtractionScript() {
|
|
48
|
-
return `
|
|
49
|
-
const contentTags = new Set(['h1','h2','h3','h4','h5','h6','p','li','article','main','section','blockquote','pre','td','th','dt','dd','figcaption']);
|
|
50
|
-
const out = [];
|
|
51
|
-
const seen = new Set();
|
|
52
|
-
const isHidden = (el) => {
|
|
53
|
-
const st = window.getComputedStyle(el);
|
|
54
|
-
return st.display === 'none' || st.visibility === 'hidden';
|
|
55
|
-
};
|
|
56
|
-
const walker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT, {
|
|
57
|
-
acceptNode: (node) => {
|
|
58
|
-
const tag = node.tagName ? node.tagName.toLowerCase() : '';
|
|
59
|
-
if (!contentTags.has(tag)) return NodeFilter.FILTER_SKIP;
|
|
60
|
-
if (isHidden(node)) return NodeFilter.FILTER_REJECT;
|
|
61
|
-
return NodeFilter.FILTER_ACCEPT;
|
|
62
|
-
}
|
|
63
|
-
});
|
|
64
|
-
let node;
|
|
65
|
-
while ((node = walker.nextNode())) {
|
|
66
|
-
const tag = node.tagName.toLowerCase();
|
|
67
|
-
const text = (node.textContent || '').replace(/\\s+/g, ' ').trim();
|
|
68
|
-
if (!text) continue;
|
|
69
|
-
if (seen.has(text)) continue;
|
|
70
|
-
seen.add(text);
|
|
71
|
-
const line = text.length > 200 ? text.slice(0, 200) + '…' : text;
|
|
72
|
-
out.push('[' + tag + '] ' + line);
|
|
73
|
-
}
|
|
74
|
-
return out.join('\\n');
|
|
75
|
-
`;
|
|
76
|
-
}
|
|
@@ -1,202 +1,202 @@
|
|
|
1
|
-
import { createRequire } from "module";
|
|
2
|
-
import readline from "readline";
|
|
3
|
-
|
|
4
|
-
const require = createRequire(import.meta.url);
|
|
5
|
-
const { chromium } = require("playwright");
|
|
6
|
-
|
|
7
|
-
const CONSOLE_CAP = 500;
|
|
8
|
-
const NETWORK_CAP = 100;
|
|
9
|
-
let maxLineChars = 400;
|
|
10
|
-
|
|
11
|
-
let browser = null;
|
|
12
|
-
let context = null;
|
|
13
|
-
let page = null;
|
|
14
|
-
let consoleBuf = [];
|
|
15
|
-
let networkBuf = [];
|
|
16
|
-
let lastUrl = "";
|
|
17
|
-
|
|
18
|
-
function pushConsole(type, text) {
|
|
19
|
-
if (consoleBuf.length >= CONSOLE_CAP) return;
|
|
20
|
-
const clean = String(text).replace(/\s+/g, " ").trim();
|
|
21
|
-
if (!clean) return;
|
|
22
|
-
const line = clean.length > maxLineChars ? clean.slice(0, maxLineChars) + "…" : clean;
|
|
23
|
-
consoleBuf.push({ type, text: line });
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function pushNetwork(err) {
|
|
27
|
-
if (networkBuf.length >= NETWORK_CAP) return;
|
|
28
|
-
networkBuf.push(err);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function drain() {
|
|
32
|
-
const consoleEvents = consoleBuf;
|
|
33
|
-
const networkEvents = networkBuf;
|
|
34
|
-
consoleBuf = [];
|
|
35
|
-
networkBuf = [];
|
|
36
|
-
return { console: consoleEvents, network: networkEvents };
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function readInt(v, fallback) {
|
|
40
|
-
const n = Number(v);
|
|
41
|
-
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async function handleLaunch(params) {
|
|
45
|
-
if (browser) return { ok: true };
|
|
46
|
-
maxLineChars = readInt(params?.maxConsoleLineChars, 400);
|
|
47
|
-
browser = await chromium.launch({
|
|
48
|
-
headless: params?.headless !== false,
|
|
49
|
-
timeout: readInt(params?.timeoutMs, 30000),
|
|
50
|
-
});
|
|
51
|
-
context = await browser.newContext({
|
|
52
|
-
viewport: params?.viewport || { width: 1280, height: 720 },
|
|
53
|
-
});
|
|
54
|
-
page = await context.newPage();
|
|
55
|
-
page.setDefaultTimeout(readInt(params?.timeoutMs, 30000));
|
|
56
|
-
|
|
57
|
-
page.on("console", (msg) => pushConsole(msg.type(), msg.text()));
|
|
58
|
-
page.on("pageerror", (err) => pushConsole("pageerror", `Page error: ${err.message}`));
|
|
59
|
-
page.on("requestfailed", (req) => {
|
|
60
|
-
const failure = req.failure();
|
|
61
|
-
pushNetwork({ method: req.method(), url: req.url(), error: failure?.errorText || "unknown" });
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
return { ok: true };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function handleGoto(params) {
|
|
68
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
69
|
-
const url = String(params?.url || "");
|
|
70
|
-
const timeoutMs = readInt(params?.timeoutMs, 15000);
|
|
71
|
-
consoleBuf = [];
|
|
72
|
-
networkBuf = [];
|
|
73
|
-
try {
|
|
74
|
-
await page.goto(url, { waitUntil: "domcontentloaded", timeout: timeoutMs });
|
|
75
|
-
lastUrl = page.url();
|
|
76
|
-
return { ok: true, data: { url: lastUrl } };
|
|
77
|
-
} catch (err) {
|
|
78
|
-
lastUrl = page.url();
|
|
79
|
-
return { ok: false, error: err.message, data: { url: lastUrl } };
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async function handleEvaluate(params) {
|
|
84
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
85
|
-
const js = String(params?.js || "");
|
|
86
|
-
try {
|
|
87
|
-
const result = await page.evaluate(js);
|
|
88
|
-
return { ok: true, data: { result } };
|
|
89
|
-
} catch (err) {
|
|
90
|
-
return { ok: false, error: err.message };
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
async function handleClose() {
|
|
95
|
-
try {
|
|
96
|
-
if (page) await page.close();
|
|
97
|
-
} catch {}
|
|
98
|
-
try {
|
|
99
|
-
if (context) await context.close();
|
|
100
|
-
} catch {}
|
|
101
|
-
try {
|
|
102
|
-
if (browser) await browser.close();
|
|
103
|
-
} catch {}
|
|
104
|
-
page = null;
|
|
105
|
-
context = null;
|
|
106
|
-
browser = null;
|
|
107
|
-
return { ok: true };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const handlers = {
|
|
111
|
-
ping: async () => ({ ok: true, data: { pong: true, runtime: process.version } }),
|
|
112
|
-
launch: handleLaunch,
|
|
113
|
-
goto: handleGoto,
|
|
114
|
-
evaluate: handleEvaluate,
|
|
115
|
-
content: async () => ({ ok: true, data: { html: page ? await page.content() : "" } }),
|
|
116
|
-
title: async () => ({ ok: true, data: { title: page ? await page.title() : "" } }),
|
|
117
|
-
url: async () => ({ ok: true, data: { url: page ? page.url() : lastUrl } }),
|
|
118
|
-
waitLoad: async (params) => {
|
|
119
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
120
|
-
try {
|
|
121
|
-
await page.waitForLoadState("domcontentloaded", {
|
|
122
|
-
timeout: readInt(params?.timeoutMs, 15000),
|
|
123
|
-
});
|
|
124
|
-
} catch {}
|
|
125
|
-
return { ok: true };
|
|
126
|
-
},
|
|
127
|
-
screenshot: async () => {
|
|
128
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
129
|
-
try {
|
|
130
|
-
const buf = await page.screenshot({ type: "png", fullPage: false });
|
|
131
|
-
return { ok: true, data: { base64: buf.toString("base64") } };
|
|
132
|
-
} catch (err) {
|
|
133
|
-
return { ok: false, error: err.message };
|
|
134
|
-
}
|
|
135
|
-
},
|
|
136
|
-
cookies: async () => {
|
|
137
|
-
if (!context) return { ok: true, data: { cookies: [] } };
|
|
138
|
-
const cookies = await context.cookies();
|
|
139
|
-
return { ok: true, data: { cookies } };
|
|
140
|
-
},
|
|
141
|
-
setCookies: async (params) => {
|
|
142
|
-
if (!context) return { ok: false, error: "No context" };
|
|
143
|
-
const cookies = Array.isArray(params?.cookies) ? params.cookies : [];
|
|
144
|
-
if (cookies.length > 0) {
|
|
145
|
-
try {
|
|
146
|
-
await context.addCookies(cookies);
|
|
147
|
-
} catch (err) {
|
|
148
|
-
return { ok: false, error: err.message };
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return { ok: true };
|
|
152
|
-
},
|
|
153
|
-
back: async (params) => {
|
|
154
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
155
|
-
try {
|
|
156
|
-
await page.goBack({
|
|
157
|
-
waitUntil: "domcontentloaded",
|
|
158
|
-
timeout: readInt(params?.timeoutMs, 15000),
|
|
159
|
-
});
|
|
160
|
-
} catch {}
|
|
161
|
-
lastUrl = page.url();
|
|
162
|
-
return { ok: true, data: { url: lastUrl } };
|
|
163
|
-
},
|
|
164
|
-
forward: async (params) => {
|
|
165
|
-
if (!page) return { ok: false, error: "No page open" };
|
|
166
|
-
try {
|
|
167
|
-
await page.goForward({
|
|
168
|
-
waitUntil: "domcontentloaded",
|
|
169
|
-
timeout: readInt(params?.timeoutMs, 15000),
|
|
170
|
-
});
|
|
171
|
-
} catch {}
|
|
172
|
-
lastUrl = page.url();
|
|
173
|
-
return { ok: true, data: { url: lastUrl } };
|
|
174
|
-
},
|
|
175
|
-
close: handleClose,
|
|
176
|
-
};
|
|
177
|
-
|
|
178
|
-
const rl = readline.createInterface({ input: process.stdin });
|
|
179
|
-
rl.on("line", async (line) => {
|
|
180
|
-
let req;
|
|
181
|
-
try {
|
|
182
|
-
req = JSON.parse(line);
|
|
183
|
-
} catch {
|
|
184
|
-
return;
|
|
185
|
-
}
|
|
186
|
-
const handler = handlers[req.cmd];
|
|
187
|
-
let resp;
|
|
188
|
-
if (!handler) {
|
|
189
|
-
resp = { id: req.id, ok: false, error: `Unknown command: ${req.cmd}`, ...drain() };
|
|
190
|
-
} else {
|
|
191
|
-
try {
|
|
192
|
-
const result = await handler(req.params || {});
|
|
193
|
-
resp = { id: req.id, ...result, ...drain() };
|
|
194
|
-
} catch (err) {
|
|
195
|
-
resp = { id: req.id, ok: false, error: err.message, ...drain() };
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
199
|
-
});
|
|
200
|
-
|
|
201
|
-
process.on("SIGTERM", () => process.exit(0));
|
|
202
|
-
process.on("SIGINT", () => process.exit(0));
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
import readline from "readline";
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const { chromium } = require("playwright");
|
|
6
|
+
|
|
7
|
+
const CONSOLE_CAP = 500;
|
|
8
|
+
const NETWORK_CAP = 100;
|
|
9
|
+
let maxLineChars = 400;
|
|
10
|
+
|
|
11
|
+
let browser = null;
|
|
12
|
+
let context = null;
|
|
13
|
+
let page = null;
|
|
14
|
+
let consoleBuf = [];
|
|
15
|
+
let networkBuf = [];
|
|
16
|
+
let lastUrl = "";
|
|
17
|
+
|
|
18
|
+
function pushConsole(type, text) {
|
|
19
|
+
if (consoleBuf.length >= CONSOLE_CAP) return;
|
|
20
|
+
const clean = String(text).replace(/\s+/g, " ").trim();
|
|
21
|
+
if (!clean) return;
|
|
22
|
+
const line = clean.length > maxLineChars ? clean.slice(0, maxLineChars) + "…" : clean;
|
|
23
|
+
consoleBuf.push({ type, text: line });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function pushNetwork(err) {
|
|
27
|
+
if (networkBuf.length >= NETWORK_CAP) return;
|
|
28
|
+
networkBuf.push(err);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function drain() {
|
|
32
|
+
const consoleEvents = consoleBuf;
|
|
33
|
+
const networkEvents = networkBuf;
|
|
34
|
+
consoleBuf = [];
|
|
35
|
+
networkBuf = [];
|
|
36
|
+
return { console: consoleEvents, network: networkEvents };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readInt(v, fallback) {
|
|
40
|
+
const n = Number(v);
|
|
41
|
+
return Number.isFinite(n) && n > 0 ? n : fallback;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async function handleLaunch(params) {
|
|
45
|
+
if (browser) return { ok: true };
|
|
46
|
+
maxLineChars = readInt(params?.maxConsoleLineChars, 400);
|
|
47
|
+
browser = await chromium.launch({
|
|
48
|
+
headless: params?.headless !== false,
|
|
49
|
+
timeout: readInt(params?.timeoutMs, 30000),
|
|
50
|
+
});
|
|
51
|
+
context = await browser.newContext({
|
|
52
|
+
viewport: params?.viewport || { width: 1280, height: 720 },
|
|
53
|
+
});
|
|
54
|
+
page = await context.newPage();
|
|
55
|
+
page.setDefaultTimeout(readInt(params?.timeoutMs, 30000));
|
|
56
|
+
|
|
57
|
+
page.on("console", (msg) => pushConsole(msg.type(), msg.text()));
|
|
58
|
+
page.on("pageerror", (err) => pushConsole("pageerror", `Page error: ${err.message}`));
|
|
59
|
+
page.on("requestfailed", (req) => {
|
|
60
|
+
const failure = req.failure();
|
|
61
|
+
pushNetwork({ method: req.method(), url: req.url(), error: failure?.errorText || "unknown" });
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
return { ok: true };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function handleGoto(params) {
|
|
68
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
69
|
+
const url = String(params?.url || "");
|
|
70
|
+
const timeoutMs = readInt(params?.timeoutMs, 15000);
|
|
71
|
+
consoleBuf = [];
|
|
72
|
+
networkBuf = [];
|
|
73
|
+
try {
|
|
74
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: timeoutMs });
|
|
75
|
+
lastUrl = page.url();
|
|
76
|
+
return { ok: true, data: { url: lastUrl } };
|
|
77
|
+
} catch (err) {
|
|
78
|
+
lastUrl = page.url();
|
|
79
|
+
return { ok: false, error: err.message, data: { url: lastUrl } };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async function handleEvaluate(params) {
|
|
84
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
85
|
+
const js = String(params?.js || "");
|
|
86
|
+
try {
|
|
87
|
+
const result = await page.evaluate(js);
|
|
88
|
+
return { ok: true, data: { result } };
|
|
89
|
+
} catch (err) {
|
|
90
|
+
return { ok: false, error: err.message };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function handleClose() {
|
|
95
|
+
try {
|
|
96
|
+
if (page) await page.close();
|
|
97
|
+
} catch {}
|
|
98
|
+
try {
|
|
99
|
+
if (context) await context.close();
|
|
100
|
+
} catch {}
|
|
101
|
+
try {
|
|
102
|
+
if (browser) await browser.close();
|
|
103
|
+
} catch {}
|
|
104
|
+
page = null;
|
|
105
|
+
context = null;
|
|
106
|
+
browser = null;
|
|
107
|
+
return { ok: true };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const handlers = {
|
|
111
|
+
ping: async () => ({ ok: true, data: { pong: true, runtime: process.version } }),
|
|
112
|
+
launch: handleLaunch,
|
|
113
|
+
goto: handleGoto,
|
|
114
|
+
evaluate: handleEvaluate,
|
|
115
|
+
content: async () => ({ ok: true, data: { html: page ? await page.content() : "" } }),
|
|
116
|
+
title: async () => ({ ok: true, data: { title: page ? await page.title() : "" } }),
|
|
117
|
+
url: async () => ({ ok: true, data: { url: page ? page.url() : lastUrl } }),
|
|
118
|
+
waitLoad: async (params) => {
|
|
119
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
120
|
+
try {
|
|
121
|
+
await page.waitForLoadState("domcontentloaded", {
|
|
122
|
+
timeout: readInt(params?.timeoutMs, 15000),
|
|
123
|
+
});
|
|
124
|
+
} catch {}
|
|
125
|
+
return { ok: true };
|
|
126
|
+
},
|
|
127
|
+
screenshot: async () => {
|
|
128
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
129
|
+
try {
|
|
130
|
+
const buf = await page.screenshot({ type: "png", fullPage: false });
|
|
131
|
+
return { ok: true, data: { base64: buf.toString("base64") } };
|
|
132
|
+
} catch (err) {
|
|
133
|
+
return { ok: false, error: err.message };
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
cookies: async () => {
|
|
137
|
+
if (!context) return { ok: true, data: { cookies: [] } };
|
|
138
|
+
const cookies = await context.cookies();
|
|
139
|
+
return { ok: true, data: { cookies } };
|
|
140
|
+
},
|
|
141
|
+
setCookies: async (params) => {
|
|
142
|
+
if (!context) return { ok: false, error: "No context" };
|
|
143
|
+
const cookies = Array.isArray(params?.cookies) ? params.cookies : [];
|
|
144
|
+
if (cookies.length > 0) {
|
|
145
|
+
try {
|
|
146
|
+
await context.addCookies(cookies);
|
|
147
|
+
} catch (err) {
|
|
148
|
+
return { ok: false, error: err.message };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return { ok: true };
|
|
152
|
+
},
|
|
153
|
+
back: async (params) => {
|
|
154
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
155
|
+
try {
|
|
156
|
+
await page.goBack({
|
|
157
|
+
waitUntil: "domcontentloaded",
|
|
158
|
+
timeout: readInt(params?.timeoutMs, 15000),
|
|
159
|
+
});
|
|
160
|
+
} catch {}
|
|
161
|
+
lastUrl = page.url();
|
|
162
|
+
return { ok: true, data: { url: lastUrl } };
|
|
163
|
+
},
|
|
164
|
+
forward: async (params) => {
|
|
165
|
+
if (!page) return { ok: false, error: "No page open" };
|
|
166
|
+
try {
|
|
167
|
+
await page.goForward({
|
|
168
|
+
waitUntil: "domcontentloaded",
|
|
169
|
+
timeout: readInt(params?.timeoutMs, 15000),
|
|
170
|
+
});
|
|
171
|
+
} catch {}
|
|
172
|
+
lastUrl = page.url();
|
|
173
|
+
return { ok: true, data: { url: lastUrl } };
|
|
174
|
+
},
|
|
175
|
+
close: handleClose,
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
179
|
+
rl.on("line", async (line) => {
|
|
180
|
+
let req;
|
|
181
|
+
try {
|
|
182
|
+
req = JSON.parse(line);
|
|
183
|
+
} catch {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const handler = handlers[req.cmd];
|
|
187
|
+
let resp;
|
|
188
|
+
if (!handler) {
|
|
189
|
+
resp = { id: req.id, ok: false, error: `Unknown command: ${req.cmd}`, ...drain() };
|
|
190
|
+
} else {
|
|
191
|
+
try {
|
|
192
|
+
const result = await handler(req.params || {});
|
|
193
|
+
resp = { id: req.id, ...result, ...drain() };
|
|
194
|
+
} catch (err) {
|
|
195
|
+
resp = { id: req.id, ok: false, error: err.message, ...drain() };
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
process.stdout.write(JSON.stringify(resp) + "\n");
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
process.on("SIGTERM", () => process.exit(0));
|
|
202
|
+
process.on("SIGINT", () => process.exit(0));
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import { readFile, writeFile, mkdir } from
|
|
2
|
-
import { join } from
|
|
1
|
+
import { readFile, writeFile, mkdir } from 'fs/promises';
|
|
2
|
+
import { join } from 'path';
|
|
3
3
|
export class CookieStore {
|
|
4
4
|
filePath;
|
|
5
5
|
constructor(cookieDir) {
|
|
6
|
-
this.filePath = join(cookieDir,
|
|
6
|
+
this.filePath = join(cookieDir, 'cookies.json');
|
|
7
7
|
}
|
|
8
8
|
async save(cookies) {
|
|
9
|
-
await mkdir(join(this.filePath,
|
|
10
|
-
await writeFile(this.filePath, JSON.stringify(cookies, null, 2),
|
|
9
|
+
await mkdir(join(this.filePath, '..'), { recursive: true });
|
|
10
|
+
await writeFile(this.filePath, JSON.stringify(cookies, null, 2), 'utf-8');
|
|
11
11
|
}
|
|
12
12
|
async load() {
|
|
13
13
|
try {
|
|
14
|
-
const data = await readFile(this.filePath,
|
|
14
|
+
const data = await readFile(this.filePath, 'utf-8');
|
|
15
15
|
return JSON.parse(data);
|
|
16
16
|
}
|
|
17
17
|
catch {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
export { BrowserModule } from
|
|
2
|
-
export { BrowserSession, BrowserActionTracker
|
|
3
|
-
export { CookieStore } from
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export { extractInteractiveElements, formatSnapshot, truncateText } from "./snapshot";
|
|
7
|
-
export { DEFAULT_BROWSER_CONFIG } from "./types";
|
|
1
|
+
export { BrowserModule } from './module';
|
|
2
|
+
export { BrowserSession, BrowserActionTracker } from './session';
|
|
3
|
+
export { CookieStore } from './cookie-store';
|
|
4
|
+
export { extractInteractiveElements, formatSnapshot } from './snapshot';
|
|
5
|
+
export { DEFAULT_BROWSER_CONFIG } from './types';
|
|
@@ -1,16 +1,15 @@
|
|
|
1
|
-
import { createBrowserTool, closeBrowserSession } from
|
|
1
|
+
import { createBrowserTool, closeBrowserSession } from '../../tools/browser';
|
|
2
2
|
export class BrowserModule {
|
|
3
|
-
name =
|
|
3
|
+
name = 'browser';
|
|
4
4
|
getSystemPromptBlock() {
|
|
5
5
|
return {
|
|
6
6
|
content: [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
'Each snapshot includes a "Content" section with the visible page text, a "Console" section with browser messages, and network errors. Use these to understand the page, detect JS errors, and verify the result of your actions.',
|
|
7
|
+
'You have a headless browser tool. Use it to navigate websites, click links, fill forms, and read page content.',
|
|
8
|
+
'Workflow: open(url) → read snapshot → click/type on element numbers → read updated snapshot.',
|
|
10
9
|
'Always start with "open" action. Use element numbers from snapshot for click/type targets.',
|
|
11
10
|
'Use "screenshot" action only if the model supports vision. Otherwise rely on the text snapshot.',
|
|
12
|
-
].join(
|
|
13
|
-
priority:
|
|
11
|
+
].join('\n'),
|
|
12
|
+
priority: 'normal',
|
|
14
13
|
essential: false,
|
|
15
14
|
estimatedTokens: 80,
|
|
16
15
|
};
|
|
@@ -20,7 +19,7 @@ export class BrowserModule {
|
|
|
20
19
|
}
|
|
21
20
|
getPlugin() {
|
|
22
21
|
return {
|
|
23
|
-
name:
|
|
22
|
+
name: 'browser',
|
|
24
23
|
onSessionEnd: () => {
|
|
25
24
|
closeBrowserSession();
|
|
26
25
|
},
|