micro-models-agent 0.28.0 → 0.28.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +314 -208
- package/package.json +1 -1
- package/dist/cli/commands.js +0 -220
- package/dist/cli/completer.js +0 -168
- package/dist/cli/index.js +0 -2
- package/dist/cli/main.js +0 -113
- package/dist/cli/repl.js +0 -987
- package/dist/cli/security-commands.js +0 -166
- package/dist/cli/setup.js +0 -229
- package/dist/config/config.js +0 -186
- package/dist/config/defaults.js +0 -91
- package/dist/config/experts.js +0 -15
- package/dist/config/index.js +0 -3
- package/dist/config/security.js +0 -193
- package/dist/config/types.js +0 -1
- package/dist/core/agent-moe.js +0 -98
- package/dist/core/agent.js +0 -461
- package/dist/core/bootstrap.js +0 -321
- package/dist/core/index.js +0 -2
- package/dist/core/prompt-builder.js +0 -55
- package/dist/core/session-logger.js +0 -122
- package/dist/core/types.js +0 -1
- package/dist/i18n/en.json +0 -461
- package/dist/i18n/index.js +0 -43
- package/dist/i18n/ru.json +0 -461
- package/dist/index.js +0 -22
- package/dist/llm/image-utils.js +0 -144
- package/dist/llm/index.js +0 -4
- package/dist/llm/model-loader.js +0 -78
- package/dist/llm/openai-compat.js +0 -324
- package/dist/llm/orchestrator.js +0 -194
- package/dist/llm/provider.js +0 -10
- package/dist/llm/response.js +0 -39
- package/dist/llm/token-counter.js +0 -39
- package/dist/llm/types.js +0 -1
- package/dist/logger/app-logger.js +0 -76
- package/dist/logger/index.js +0 -1
- package/dist/migration/backup.js +0 -45
- package/dist/migration/detect.js +0 -50
- package/dist/migration/index.js +0 -2
- package/dist/modules/browser/actions.js +0 -46
- package/dist/modules/browser/cookie-store.js +0 -24
- package/dist/modules/browser/index.js +0 -5
- package/dist/modules/browser/module.js +0 -28
- package/dist/modules/browser/session.js +0 -287
- package/dist/modules/browser/snapshot.js +0 -114
- package/dist/modules/browser/types.js +0 -9
- package/dist/modules/context/history.js +0 -15
- package/dist/modules/context/index.js +0 -1
- package/dist/modules/context/manager.js +0 -240
- package/dist/modules/execution/auditor.js +0 -72
- package/dist/modules/execution/index.js +0 -6
- package/dist/modules/execution/module.js +0 -337
- package/dist/modules/execution/moe-executor.js +0 -209
- package/dist/modules/execution/plan-validator.js +0 -153
- package/dist/modules/execution/planner.js +0 -35
- package/dist/modules/execution/stuck-detector.js +0 -134
- package/dist/modules/execution/tracker.js +0 -53
- package/dist/modules/execution/types.js +0 -1
- package/dist/modules/execution/verifier.js +0 -149
- package/dist/modules/hallucination/confidence.js +0 -54
- package/dist/modules/hallucination/consistency.js +0 -60
- package/dist/modules/hallucination/detector.js +0 -41
- package/dist/modules/hallucination/factual.js +0 -170
- package/dist/modules/hallucination/index.js +0 -4
- package/dist/modules/index.js +0 -5
- package/dist/modules/indexer/cache.js +0 -38
- package/dist/modules/indexer/index.js +0 -3
- package/dist/modules/indexer/module.js +0 -192
- package/dist/modules/indexer/walker.js +0 -101
- package/dist/modules/mcp/client.js +0 -393
- package/dist/modules/mcp/index.js +0 -3
- package/dist/modules/mcp/module.js +0 -146
- package/dist/modules/mcp/registry.js +0 -15
- package/dist/modules/memory/index.js +0 -1
- package/dist/modules/memory/module.js +0 -48
- package/dist/modules/memory/search.js +0 -40
- package/dist/modules/memory/store.js +0 -65
- package/dist/modules/pipelines/engine.js +0 -60
- package/dist/modules/pipelines/index.js +0 -3
- package/dist/modules/pipelines/parser.js +0 -53
- package/dist/modules/pipelines/template.js +0 -14
- package/dist/modules/plugins/builtin/lint-on-write.js +0 -121
- package/dist/modules/plugins/builtin/notify.js +0 -8
- package/dist/modules/plugins/index.js +0 -1
- package/dist/modules/plugins/loader.js +0 -28
- package/dist/modules/plugins/manager.js +0 -161
- package/dist/modules/plugins/types.js +0 -1
- package/dist/modules/processes/detect.js +0 -34
- package/dist/modules/processes/index.js +0 -3
- package/dist/modules/processes/registry.js +0 -148
- package/dist/modules/processes/runner.js +0 -124
- package/dist/modules/registry.js +0 -45
- package/dist/modules/security/audit-log.js +0 -116
- package/dist/modules/security/audit-notifier.js +0 -292
- package/dist/modules/security/command-validator.js +0 -185
- package/dist/modules/security/content-scanner.js +0 -52
- package/dist/modules/security/data-sanitizer.js +0 -97
- package/dist/modules/security/encryption.js +0 -240
- package/dist/modules/security/index.js +0 -14
- package/dist/modules/security/network-validator.js +0 -79
- package/dist/modules/security/path-validator.js +0 -155
- package/dist/modules/security/rate-limiter.js +0 -119
- package/dist/modules/security/security-policies.js +0 -393
- package/dist/modules/security/session-encryption.js +0 -193
- package/dist/modules/security/session-isolation.js +0 -95
- package/dist/modules/session/index.js +0 -3
- package/dist/modules/session/manager.js +0 -167
- package/dist/modules/session/module.js +0 -24
- package/dist/modules/session/store.js +0 -174
- package/dist/modules/session/types.js +0 -1
- package/dist/modules/skills/index.js +0 -3
- package/dist/modules/skills/loader.js +0 -72
- package/dist/modules/skills/matcher.js +0 -27
- package/dist/modules/skills/module.js +0 -143
- package/dist/modules/types.js +0 -1
- package/dist/modules/updater/checker.js +0 -32
- package/dist/modules/updater/index.js +0 -1
- package/dist/modules/user-profile/compressor.js +0 -16
- package/dist/modules/user-profile/index.js +0 -1
- package/dist/modules/user-profile/profile.js +0 -68
- package/dist/tools/approve.js +0 -32
- package/dist/tools/attach-image.js +0 -89
- package/dist/tools/bash.js +0 -140
- package/dist/tools/browser.js +0 -97
- package/dist/tools/create-dir.js +0 -56
- package/dist/tools/delete-file.js +0 -63
- package/dist/tools/edit-file.js +0 -77
- package/dist/tools/executor.js +0 -95
- package/dist/tools/file-info.js +0 -45
- package/dist/tools/filter-tools.js +0 -10
- package/dist/tools/glob-tool.js +0 -26
- package/dist/tools/grep-tool.js +0 -64
- package/dist/tools/index.js +0 -52
- package/dist/tools/list-dir.js +0 -47
- package/dist/tools/load-skill.js +0 -48
- package/dist/tools/mcp-call.js +0 -68
- package/dist/tools/move-file.js +0 -84
- package/dist/tools/path-utils.js +0 -51
- package/dist/tools/pipeline-run.js +0 -144
- package/dist/tools/preview.js +0 -2
- package/dist/tools/process-kill.js +0 -29
- package/dist/tools/process-list.js +0 -38
- package/dist/tools/process-log.js +0 -41
- package/dist/tools/question.js +0 -142
- package/dist/tools/read-file.js +0 -73
- package/dist/tools/recall.js +0 -110
- package/dist/tools/registry.js +0 -36
- package/dist/tools/remember.js +0 -67
- package/dist/tools/scope-check.js +0 -30
- package/dist/tools/search-history.js +0 -64
- package/dist/tools/subagent.js +0 -142
- package/dist/tools/types.js +0 -1
- package/dist/tools/user-input.js +0 -123
- package/dist/tools/web-browse.js +0 -57
- package/dist/tools/web-fetch.js +0 -72
- package/dist/tools/web-search.js +0 -59
- package/dist/tools/write-file.js +0 -80
- package/dist/ui/box.js +0 -81
- package/dist/ui/colors.js +0 -4
- package/dist/ui/diff.js +0 -185
- package/dist/ui/index.js +0 -6
- package/dist/ui/md-formatter.js +0 -212
- package/dist/ui/output.js +0 -13
- package/dist/ui/renderer.js +0 -141
- package/dist/ui/spinner.js +0 -70
- package/dist/ui/table.js +0 -144
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import { homedir, hostname, platform, type } from 'os';
|
|
4
|
-
import { env } from 'process';
|
|
5
|
-
import { ProfileCompressor } from './compressor';
|
|
6
|
-
export class UserProfile {
|
|
7
|
-
profileDir;
|
|
8
|
-
info = null;
|
|
9
|
-
preferences = {};
|
|
10
|
-
constructor(profileDir) {
|
|
11
|
-
this.profileDir = profileDir;
|
|
12
|
-
}
|
|
13
|
-
collect() {
|
|
14
|
-
this.info = {
|
|
15
|
-
platform: platform(),
|
|
16
|
-
os: `${type()} ${hostname()}`,
|
|
17
|
-
hostname: hostname(),
|
|
18
|
-
shell: env.SHELL || env.ComSpec || 'unknown',
|
|
19
|
-
home: homedir(),
|
|
20
|
-
nodeVersion: process.version,
|
|
21
|
-
preferences: { ...this.preferences },
|
|
22
|
-
};
|
|
23
|
-
return this.info;
|
|
24
|
-
}
|
|
25
|
-
save() {
|
|
26
|
-
if (!existsSync(this.profileDir)) {
|
|
27
|
-
mkdirSync(this.profileDir, { recursive: true });
|
|
28
|
-
}
|
|
29
|
-
writeFileSync(join(this.profileDir, 'profile.json'), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), 'utf-8');
|
|
30
|
-
}
|
|
31
|
-
load() {
|
|
32
|
-
const path = join(this.profileDir, 'profile.json');
|
|
33
|
-
if (!existsSync(path))
|
|
34
|
-
return null;
|
|
35
|
-
try {
|
|
36
|
-
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
37
|
-
this.info = {
|
|
38
|
-
platform: data.platform,
|
|
39
|
-
os: data.os,
|
|
40
|
-
hostname: data.hostname,
|
|
41
|
-
shell: data.shell,
|
|
42
|
-
home: data.home,
|
|
43
|
-
nodeVersion: data.nodeVersion,
|
|
44
|
-
preferences: data.preferences || {},
|
|
45
|
-
};
|
|
46
|
-
this.preferences = data.preferences || {};
|
|
47
|
-
return this.info;
|
|
48
|
-
}
|
|
49
|
-
catch {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
getInfo() {
|
|
54
|
-
return this.info;
|
|
55
|
-
}
|
|
56
|
-
setPreference(key, value) {
|
|
57
|
-
this.preferences[key] = value;
|
|
58
|
-
}
|
|
59
|
-
getPreference(key) {
|
|
60
|
-
return this.preferences[key];
|
|
61
|
-
}
|
|
62
|
-
compress() {
|
|
63
|
-
if (!this.info)
|
|
64
|
-
this.collect();
|
|
65
|
-
const compressor = new ProfileCompressor();
|
|
66
|
-
return compressor.compress(this.info);
|
|
67
|
-
}
|
|
68
|
-
}
|
package/dist/tools/approve.js
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { t } from "../i18n/index";
|
|
2
|
-
import { askChoice } from "./user-input";
|
|
3
|
-
export const approveTool = {
|
|
4
|
-
name: "approve",
|
|
5
|
-
description: "Request user approval for an action. The user picks Yes or No from a menu.",
|
|
6
|
-
tags: ["core"],
|
|
7
|
-
interactive: true,
|
|
8
|
-
parameters: {
|
|
9
|
-
type: "object",
|
|
10
|
-
properties: {
|
|
11
|
-
action: {
|
|
12
|
-
type: "string",
|
|
13
|
-
description: "Description of the action requiring approval",
|
|
14
|
-
},
|
|
15
|
-
},
|
|
16
|
-
required: ["action"],
|
|
17
|
-
},
|
|
18
|
-
handler: async (ctx, args) => {
|
|
19
|
-
if (ctx.exitOnComplete) {
|
|
20
|
-
return { success: false, output: t("tool.interactive_disabled") };
|
|
21
|
-
}
|
|
22
|
-
const action = String(args.action || "");
|
|
23
|
-
const indexes = await askChoice(t("tool.approve_prompt", { action }), [
|
|
24
|
-
{ label: t("tool.approve_yes"), description: t("tool.approve_yes_desc") },
|
|
25
|
-
{ label: t("tool.approve_no"), description: t("tool.approve_no_desc") },
|
|
26
|
-
]);
|
|
27
|
-
if (indexes[0] === 0) {
|
|
28
|
-
return { success: true, output: t("tool.approved") };
|
|
29
|
-
}
|
|
30
|
-
return { success: false, output: t("tool.rejected") };
|
|
31
|
-
},
|
|
32
|
-
};
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "fs";
|
|
2
|
-
import { resolve } from "path";
|
|
3
|
-
import { t } from "../i18n/index";
|
|
4
|
-
import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl } from "../llm/image-utils";
|
|
5
|
-
import { logSecurityBlock } from "../modules/security/audit-log";
|
|
6
|
-
export const attachImageTool = {
|
|
7
|
-
name: "attach_image",
|
|
8
|
-
description: "Attach an image to the conversation from a file path, URL, or clipboard. The image will be included in the next message sent to the model. Supports PNG, JPEG, GIF, WebP.",
|
|
9
|
-
tags: ["vision", "image"],
|
|
10
|
-
parameters: {
|
|
11
|
-
type: "object",
|
|
12
|
-
properties: {
|
|
13
|
-
source: {
|
|
14
|
-
type: "string",
|
|
15
|
-
description: 'Image source: file path (e.g. "./screenshot.png"), URL (e.g. "https://example.com/img.png"), or "clipboard" to read from system clipboard.',
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
required: ["source"],
|
|
19
|
-
},
|
|
20
|
-
handler: async (ctx, args) => {
|
|
21
|
-
const source = String(args.source ?? "").trim();
|
|
22
|
-
if (!source) {
|
|
23
|
-
return { success: false, output: t("image.source_required") };
|
|
24
|
-
}
|
|
25
|
-
try {
|
|
26
|
-
let dataUrl;
|
|
27
|
-
if (source.toLowerCase() === "clipboard") {
|
|
28
|
-
const clipBuf = await readClipboardImage();
|
|
29
|
-
if (!clipBuf) {
|
|
30
|
-
return {
|
|
31
|
-
success: false,
|
|
32
|
-
output: t("image.clipboard_empty"),
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const result = await bufferToDataUrl(clipBuf);
|
|
36
|
-
dataUrl = result.dataUrl;
|
|
37
|
-
}
|
|
38
|
-
else if (source.startsWith("http://") || source.startsWith("https://")) {
|
|
39
|
-
const result = await loadUrlAsDataUrl(source);
|
|
40
|
-
dataUrl = result.dataUrl;
|
|
41
|
-
}
|
|
42
|
-
else {
|
|
43
|
-
const absPath = resolve(ctx.baseDir, source);
|
|
44
|
-
if (!existsSync(absPath)) {
|
|
45
|
-
return {
|
|
46
|
-
success: false,
|
|
47
|
-
output: t("image.not_found", { path: source }),
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
// Security: check path scope
|
|
51
|
-
if (ctx.scope) {
|
|
52
|
-
const { isPathInScope } = await import("../modules/security/path-validator");
|
|
53
|
-
const validation = isPathInScope(ctx.baseDir, absPath, ctx.scope);
|
|
54
|
-
if (!validation.allowed) {
|
|
55
|
-
logSecurityBlock(ctx.sessionId, "file_read", validation.reason ?? "attach_image out of scope", absPath);
|
|
56
|
-
return {
|
|
57
|
-
success: false,
|
|
58
|
-
output: t("file.path_not_allowed", { path: source }),
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const result = await loadFileAsDataUrl(absPath);
|
|
63
|
-
dataUrl = result.dataUrl;
|
|
64
|
-
}
|
|
65
|
-
// Add the image as a pending content part on the context manager
|
|
66
|
-
if (!ctx.contextManager) {
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
output: t("image.no_context"),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
ctx.contextManager.addPendingImage({
|
|
73
|
-
type: "image_url",
|
|
74
|
-
image_url: { url: dataUrl },
|
|
75
|
-
});
|
|
76
|
-
const sizeKb = Math.round((dataUrl.length * 3) / 4 / 1024);
|
|
77
|
-
return {
|
|
78
|
-
success: true,
|
|
79
|
-
output: t("image.attached", { source, size: `${sizeKb} KB` }),
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
catch (err) {
|
|
83
|
-
return {
|
|
84
|
-
success: false,
|
|
85
|
-
output: t("image.error", { message: err.message }),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
};
|
package/dist/tools/bash.js
DELETED
|
@@ -1,140 +0,0 @@
|
|
|
1
|
-
import { isCommandAllowed, sanitizeCommandForLog } from '../modules/security/command-validator';
|
|
2
|
-
import { logBashCommand, logSecurityBlock } from '../modules/security/audit-log';
|
|
3
|
-
import { getSessionSecurityConfig } from '../modules/security/session-isolation';
|
|
4
|
-
import { DEFAULT_SECURITY_CONFIG } from '../config/security';
|
|
5
|
-
import { runCommand, processRegistry, isLongRunningCommand } from '../modules/processes';
|
|
6
|
-
import { t } from '../i18n/index';
|
|
7
|
-
import { platform } from 'os';
|
|
8
|
-
import { MAX_PREVIEW_LINES } from './preview';
|
|
9
|
-
const BASH_TIMEOUT_MS = 120_000;
|
|
10
|
-
function adaptCommandForWindows(command) {
|
|
11
|
-
if (platform() !== 'win32')
|
|
12
|
-
return command;
|
|
13
|
-
// Windows mkdir does not support -p flag, but creates intermediate dirs by default
|
|
14
|
-
const trimmed = command.trim();
|
|
15
|
-
if (trimmed.startsWith('mkdir -p ')) {
|
|
16
|
-
return trimmed.replace(/^mkdir -p /, 'mkdir ');
|
|
17
|
-
}
|
|
18
|
-
if (trimmed === 'mkdir -p' || trimmed.startsWith('mkdir -p ')) {
|
|
19
|
-
return trimmed.replace(/mkdir -p/g, 'mkdir');
|
|
20
|
-
}
|
|
21
|
-
// No encoding adaptation needed — runner.ts handles UTF-8 decoding
|
|
22
|
-
return command;
|
|
23
|
-
}
|
|
24
|
-
/** Common Unix → Windows command mapping for error hints. */
|
|
25
|
-
const UNIX_TO_WIN_HINTS = {
|
|
26
|
-
'ls': 'Use "dir" or the list_dir tool instead.',
|
|
27
|
-
'pwd': 'Use "echo %cd%" or the file_info tool instead.',
|
|
28
|
-
'cat': 'Use "type" or the read_file tool instead.',
|
|
29
|
-
'cp': 'Use "copy" or the move_file tool instead.',
|
|
30
|
-
'mv': 'Use "move" or the move_file tool instead.',
|
|
31
|
-
'rm': 'Use "del" or the delete_file tool instead.',
|
|
32
|
-
'grep': 'Use "findstr" or the grep tool instead.',
|
|
33
|
-
'chmod': 'Use icacls or the chmod tool instead.',
|
|
34
|
-
'touch': 'Use type nul > file or the write_file tool instead.',
|
|
35
|
-
'find': 'Use "dir /s" or the glob tool instead.',
|
|
36
|
-
'head': 'Use the read_file tool with offset/limit instead.',
|
|
37
|
-
'tail': 'Use the read_file tool instead.',
|
|
38
|
-
'wc': 'Use the read_file tool instead.',
|
|
39
|
-
'diff': 'Use the diff tool instead.',
|
|
40
|
-
'which': 'Use "where" instead.',
|
|
41
|
-
'echo': 'echo works on Windows, but avoid pipes (|).',
|
|
42
|
-
};
|
|
43
|
-
export const bashTool = {
|
|
44
|
-
name: 'bash',
|
|
45
|
-
description: 'Execute a shell command and return its output. Use for running tests, build, git, and shell operations. Long-running commands (dev servers, watchers) start in the background and return a process id immediately — manage them with process_list, process_log, process_kill. Set background=true to force background execution.',
|
|
46
|
-
tags: ['shell', 'code'],
|
|
47
|
-
parameters: {
|
|
48
|
-
type: 'object',
|
|
49
|
-
properties: {
|
|
50
|
-
command: { type: 'string', description: 'Shell command to execute' },
|
|
51
|
-
workdir: { type: 'string', description: 'Working directory (default: baseDir)' },
|
|
52
|
-
background: { type: 'boolean', description: 'Start the command in the background and return immediately with a process id (default: auto-detect long-running commands)' },
|
|
53
|
-
},
|
|
54
|
-
required: ['command'],
|
|
55
|
-
},
|
|
56
|
-
handler: async (ctx, args) => {
|
|
57
|
-
const originalCommand = String(args.command);
|
|
58
|
-
const command = adaptCommandForWindows(originalCommand);
|
|
59
|
-
const workdir = args.workdir ? String(args.workdir) : ctx.baseDir;
|
|
60
|
-
// Get session-specific security config with defaults
|
|
61
|
-
const appConfig = ctx.config || {};
|
|
62
|
-
const fullSecurityConfig = ctx.sessionContext
|
|
63
|
-
? getSessionSecurityConfig(appConfig, ctx.sessionContext)
|
|
64
|
-
: appConfig.security || DEFAULT_SECURITY_CONFIG;
|
|
65
|
-
const securityConfig = fullSecurityConfig.bash || DEFAULT_SECURITY_CONFIG.bash;
|
|
66
|
-
const validation = isCommandAllowed(command, securityConfig);
|
|
67
|
-
if (!validation.allowed) {
|
|
68
|
-
// Log security block
|
|
69
|
-
logSecurityBlock(ctx.sessionId, "bash_command", validation.reason || "Command blocked by security policy", sanitizeCommandForLog(originalCommand));
|
|
70
|
-
return {
|
|
71
|
-
success: false,
|
|
72
|
-
output: `[SECURITY BLOCKED] Command is not allowed: ${validation.reason}\nHint: Use the "workdir" parameter to run commands in a specific directory instead of "cd dir && cmd". Run one command per tool call.`,
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
// Log command execution if enabled
|
|
76
|
-
if (securityConfig?.logCommands) {
|
|
77
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), false, // Will be updated after execution
|
|
78
|
-
`Working directory: ${workdir}`);
|
|
79
|
-
}
|
|
80
|
-
const background = args.background === true || isLongRunningCommand(command);
|
|
81
|
-
if (background) {
|
|
82
|
-
const entry = processRegistry.start(command, workdir, ctx.sessionId);
|
|
83
|
-
if (securityConfig?.logCommands) {
|
|
84
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), true, `Started in background: ${entry.id} (PID ${entry.pid})`);
|
|
85
|
-
}
|
|
86
|
-
const explicit = args.background === true;
|
|
87
|
-
return {
|
|
88
|
-
success: true,
|
|
89
|
-
output: `${t("proc.started", {
|
|
90
|
-
id: entry.id,
|
|
91
|
-
pid: entry.pid,
|
|
92
|
-
command,
|
|
93
|
-
})}${explicit ? "" : `\n${t("proc.detected_hint")}`}\n${t("proc.manage_hint", {
|
|
94
|
-
id: entry.id,
|
|
95
|
-
})}`,
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
try {
|
|
99
|
-
const res = await runCommand(command, {
|
|
100
|
-
cwd: workdir,
|
|
101
|
-
callId: ctx.activeCallId,
|
|
102
|
-
timeoutMs: BASH_TIMEOUT_MS,
|
|
103
|
-
});
|
|
104
|
-
const parts = [res.stdout.trimEnd(), res.stderr.trimEnd()].filter(Boolean);
|
|
105
|
-
let output = parts.join("\n");
|
|
106
|
-
if (res.timedOut) {
|
|
107
|
-
output = `${output ? output + "\n" : ""}${t("proc.timed_out", {
|
|
108
|
-
ms: BASH_TIMEOUT_MS,
|
|
109
|
-
})}`;
|
|
110
|
-
}
|
|
111
|
-
else if (!output && res.code !== 0) {
|
|
112
|
-
output = `(exit code ${res.code})`;
|
|
113
|
-
}
|
|
114
|
-
// On Windows, hint about Unix commands that don't work
|
|
115
|
-
if (platform() === 'win32' && res.code !== 0) {
|
|
116
|
-
const firstWord = command.trim().split(/\s+/)[0]?.split(/[\\/]/).pop();
|
|
117
|
-
const hint = firstWord ? UNIX_TO_WIN_HINTS[firstWord] : undefined;
|
|
118
|
-
if (hint) {
|
|
119
|
-
output = `${output}\n\nHint: "${firstWord}" may not work on Windows. ${hint}`;
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Update audit log with result
|
|
123
|
-
if (securityConfig?.logCommands) {
|
|
124
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), res.code === 0, `Working directory: ${workdir}, Output length: ${res.stdout.length}`);
|
|
125
|
-
}
|
|
126
|
-
const lines = output.split('\n');
|
|
127
|
-
if (lines.length > MAX_PREVIEW_LINES) {
|
|
128
|
-
output = lines.slice(0, MAX_PREVIEW_LINES).join('\n') + `\n... (${lines.length - MAX_PREVIEW_LINES} more lines)`;
|
|
129
|
-
}
|
|
130
|
-
return { success: res.code === 0, output };
|
|
131
|
-
}
|
|
132
|
-
catch (e) {
|
|
133
|
-
// Update audit log with failure
|
|
134
|
-
if (securityConfig?.logCommands) {
|
|
135
|
-
logBashCommand(ctx.sessionId, sanitizeCommandForLog(command), false, `Working directory: ${workdir}, Error: ${e.message?.slice(0, 100) || ""}`);
|
|
136
|
-
}
|
|
137
|
-
return { success: false, output: e.message || String(e) };
|
|
138
|
-
}
|
|
139
|
-
},
|
|
140
|
-
};
|
package/dist/tools/browser.js
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { BrowserSession } from '../modules/browser/session';
|
|
2
|
-
import { DEFAULT_BROWSER_CONFIG } from '../modules/browser/types';
|
|
3
|
-
import { t } from '../i18n/index';
|
|
4
|
-
import { join } from 'path';
|
|
5
|
-
let session = null;
|
|
6
|
-
function getSession(ctx) {
|
|
7
|
-
if (!session) {
|
|
8
|
-
const cookieDir = join(ctx.baseDir, '.mma', 'browser');
|
|
9
|
-
session = new BrowserSession({
|
|
10
|
-
...DEFAULT_BROWSER_CONFIG,
|
|
11
|
-
headless: ctx.config.browser?.headless ?? true,
|
|
12
|
-
maxElements: ctx.config.browser?.maxElements ?? 30,
|
|
13
|
-
navigationTimeout: ctx.config.browser?.navigationTimeout ?? 15000,
|
|
14
|
-
viewportWidth: ctx.config.browser?.viewportWidth ?? 1280,
|
|
15
|
-
viewportHeight: ctx.config.browser?.viewportHeight ?? 720,
|
|
16
|
-
cookieDir,
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
return session;
|
|
20
|
-
}
|
|
21
|
-
export function formatScreenshotForTextModel(snapshot) {
|
|
22
|
-
return `${t('tool.screenshot_unavailable')}\n\n${snapshot}`;
|
|
23
|
-
}
|
|
24
|
-
export function formatScreenshotResult(snapshot, screenshot, supportsVision) {
|
|
25
|
-
if (!supportsVision) {
|
|
26
|
-
return formatScreenshotForTextModel(snapshot);
|
|
27
|
-
}
|
|
28
|
-
const base64 = screenshot.toString('base64');
|
|
29
|
-
return `${snapshot}\n\n[Screenshot: data:image/png;base64,${base64}]`;
|
|
30
|
-
}
|
|
31
|
-
export function createBrowserTool() {
|
|
32
|
-
return {
|
|
33
|
-
name: 'browser',
|
|
34
|
-
tags: ['browser', 'vision'],
|
|
35
|
-
description: [
|
|
36
|
-
'Control a headless browser. Navigate pages, click elements, type text, scroll, take screenshots.',
|
|
37
|
-
'Returns a numbered list of interactive elements — use element numbers as targets for click/type.',
|
|
38
|
-
'Actions: open (url), click (target), type (target, text), scroll (direction), back, forward, screenshot, snapshot, close, wait (ms).',
|
|
39
|
-
].join(' '),
|
|
40
|
-
parameters: {
|
|
41
|
-
type: 'object',
|
|
42
|
-
properties: {
|
|
43
|
-
action: {
|
|
44
|
-
type: 'string',
|
|
45
|
-
enum: ['open', 'click', 'type', 'scroll', 'back', 'forward', 'screenshot', 'snapshot', 'close', 'wait'],
|
|
46
|
-
description: 'Browser action to perform',
|
|
47
|
-
},
|
|
48
|
-
url: {
|
|
49
|
-
type: 'string',
|
|
50
|
-
description: 'URL to open (for action "open")',
|
|
51
|
-
},
|
|
52
|
-
target: {
|
|
53
|
-
type: 'number',
|
|
54
|
-
description: 'Element number from snapshot (for action "click" or "type")',
|
|
55
|
-
},
|
|
56
|
-
text: {
|
|
57
|
-
type: 'string',
|
|
58
|
-
description: 'Text to type (for action "type")',
|
|
59
|
-
},
|
|
60
|
-
direction: {
|
|
61
|
-
type: 'string',
|
|
62
|
-
enum: ['up', 'down', 'top', 'bottom'],
|
|
63
|
-
description: 'Scroll direction (for action "scroll", default "down")',
|
|
64
|
-
},
|
|
65
|
-
ms: {
|
|
66
|
-
type: 'number',
|
|
67
|
-
description: 'Milliseconds to wait (for action "wait", default 1000)',
|
|
68
|
-
},
|
|
69
|
-
},
|
|
70
|
-
required: ['action'],
|
|
71
|
-
},
|
|
72
|
-
handler: async (ctx, args) => {
|
|
73
|
-
const action = String(args.action || '');
|
|
74
|
-
if (!action) {
|
|
75
|
-
return { success: false, output: t('tool.action_required') };
|
|
76
|
-
}
|
|
77
|
-
const s = getSession(ctx);
|
|
78
|
-
const result = await s.execute(action, args);
|
|
79
|
-
const supportsVision = ctx.config.model.includes('vision') ||
|
|
80
|
-
ctx.config.model.includes('gpt-4o') ||
|
|
81
|
-
ctx.config.model.includes('claude');
|
|
82
|
-
if (result.screenshot) {
|
|
83
|
-
return {
|
|
84
|
-
success: result.success,
|
|
85
|
-
output: formatScreenshotResult(result.output, result.screenshot, supportsVision),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
return { success: result.success, output: result.output };
|
|
89
|
-
},
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
export function closeBrowserSession() {
|
|
93
|
-
if (session) {
|
|
94
|
-
session.close().catch(() => { });
|
|
95
|
-
session = null;
|
|
96
|
-
}
|
|
97
|
-
}
|
package/dist/tools/create-dir.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { mkdirSync, existsSync } from "fs";
|
|
2
|
-
import { t } from "../i18n/index";
|
|
3
|
-
import { isPathWritable } from "../modules/security/path-validator";
|
|
4
|
-
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
5
|
-
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
6
|
-
import { safeResolvePath } from "./path-utils";
|
|
7
|
-
export const createDirTool = {
|
|
8
|
-
name: "create_dir",
|
|
9
|
-
description: "Create a directory (and any intermediate directories).",
|
|
10
|
-
tags: ["file"],
|
|
11
|
-
parameters: {
|
|
12
|
-
type: "object",
|
|
13
|
-
properties: {
|
|
14
|
-
path: { type: "string", description: "Directory path" },
|
|
15
|
-
},
|
|
16
|
-
required: ["path"],
|
|
17
|
-
},
|
|
18
|
-
handler: async (ctx, args) => {
|
|
19
|
-
const path = String(args.path);
|
|
20
|
-
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
21
|
-
// Get session-specific security config
|
|
22
|
-
const securityConfig = ctx.sessionContext
|
|
23
|
-
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
24
|
-
: ctx.config.security;
|
|
25
|
-
// Check file operations limit
|
|
26
|
-
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
27
|
-
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
28
|
-
if (currentCount >= maxFileOps) {
|
|
29
|
-
logSecurityBlock(ctx.sessionId, "file_write", `Maximum file operations (${maxFileOps}) exceeded`, path);
|
|
30
|
-
return {
|
|
31
|
-
success: false,
|
|
32
|
-
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
// Check path permissions
|
|
36
|
-
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
37
|
-
if (!scopeCheck.allowed) {
|
|
38
|
-
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
39
|
-
return {
|
|
40
|
-
success: false,
|
|
41
|
-
output: t("file.path_not_allowed", {
|
|
42
|
-
path: `${path} — ${scopeCheck.reason}`,
|
|
43
|
-
}),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
if (!existsSync(resolved)) {
|
|
47
|
-
mkdirSync(resolved, { recursive: true });
|
|
48
|
-
}
|
|
49
|
-
// Increment file operations counter
|
|
50
|
-
ctx.fileOperationsCount = currentCount + 1;
|
|
51
|
-
// Log directory creation
|
|
52
|
-
logFileWrite(ctx.sessionId, path, true, "Directory created");
|
|
53
|
-
ctx.trackCreatedPath?.(path);
|
|
54
|
-
return { success: true, output: t("file.created", { path }) };
|
|
55
|
-
},
|
|
56
|
-
};
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { unlinkSync, existsSync, statSync, readFileSync } from "fs";
|
|
2
|
-
import { t } from "../i18n/index";
|
|
3
|
-
import { isPathWritable } from "../modules/security/path-validator";
|
|
4
|
-
import { logFileDelete, logSecurityBlock } from "../modules/security/audit-log";
|
|
5
|
-
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
6
|
-
import { generateDeleteDiff } from "../ui/diff";
|
|
7
|
-
import { safeResolvePath } from "./path-utils";
|
|
8
|
-
export const deleteFileTool = {
|
|
9
|
-
name: "delete_file",
|
|
10
|
-
description: "Delete a file from the filesystem.",
|
|
11
|
-
tags: ["file"],
|
|
12
|
-
parameters: {
|
|
13
|
-
type: "object",
|
|
14
|
-
properties: {
|
|
15
|
-
path: { type: "string", description: "File path" },
|
|
16
|
-
},
|
|
17
|
-
required: ["path"],
|
|
18
|
-
},
|
|
19
|
-
handler: async (ctx, args) => {
|
|
20
|
-
const path = String(args.path);
|
|
21
|
-
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
22
|
-
// Get session-specific security config
|
|
23
|
-
const securityConfig = ctx.sessionContext
|
|
24
|
-
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
25
|
-
: ctx.config.security;
|
|
26
|
-
// Check file operations limit
|
|
27
|
-
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
28
|
-
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
29
|
-
if (currentCount >= maxFileOps) {
|
|
30
|
-
logSecurityBlock(ctx.sessionId, "file_delete", `Maximum file operations (${maxFileOps}) exceeded`, path);
|
|
31
|
-
return {
|
|
32
|
-
success: false,
|
|
33
|
-
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
// Check path permissions
|
|
37
|
-
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
38
|
-
if (!scopeCheck.allowed) {
|
|
39
|
-
logSecurityBlock(ctx.sessionId, "file_delete", scopeCheck.reason || "Path not allowed", path);
|
|
40
|
-
return {
|
|
41
|
-
success: false,
|
|
42
|
-
output: t("file.path_not_allowed", {
|
|
43
|
-
path: `${path} — ${scopeCheck.reason}`,
|
|
44
|
-
}),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
if (!existsSync(resolved)) {
|
|
48
|
-
return { success: false, output: t("file.notfound", { path }) };
|
|
49
|
-
}
|
|
50
|
-
if (statSync(resolved).isDirectory()) {
|
|
51
|
-
return { success: false, output: t("file.is_directory", { path }) };
|
|
52
|
-
}
|
|
53
|
-
const content = readFileSync(resolved, "utf-8");
|
|
54
|
-
unlinkSync(resolved);
|
|
55
|
-
const diff = generateDeleteDiff(content);
|
|
56
|
-
// Increment file operations counter
|
|
57
|
-
ctx.fileOperationsCount = currentCount + 1;
|
|
58
|
-
// Log successful file deletion
|
|
59
|
-
logFileDelete(ctx.sessionId, path, true);
|
|
60
|
-
ctx.trackDeletedPath?.(path);
|
|
61
|
-
return { success: true, output: t("file.deleted", { path }), diff };
|
|
62
|
-
},
|
|
63
|
-
};
|
package/dist/tools/edit-file.js
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync } from "fs";
|
|
2
|
-
import { t } from "../i18n/index";
|
|
3
|
-
import { isPathWritable } from "../modules/security/path-validator";
|
|
4
|
-
import { scanContent } from "../modules/security/content-scanner";
|
|
5
|
-
import { logFileWrite, logSecurityBlock } from "../modules/security/audit-log";
|
|
6
|
-
import { getSessionSecurityConfig } from "../modules/security/session-isolation";
|
|
7
|
-
import { generateDiff } from "../ui/diff";
|
|
8
|
-
import { safeResolvePath } from "./path-utils";
|
|
9
|
-
export const editFileTool = {
|
|
10
|
-
name: "edit_file",
|
|
11
|
-
description: "Find and replace text in an existing file. Uses exact string match (not regex).",
|
|
12
|
-
tags: ["file", "code"],
|
|
13
|
-
parameters: {
|
|
14
|
-
type: "object",
|
|
15
|
-
properties: {
|
|
16
|
-
path: { type: "string", description: "File path" },
|
|
17
|
-
old: { type: "string", description: "Text to replace" },
|
|
18
|
-
new: { type: "string", description: "Replacement text" },
|
|
19
|
-
},
|
|
20
|
-
required: ["path", "old", "new"],
|
|
21
|
-
},
|
|
22
|
-
handler: async (ctx, args) => {
|
|
23
|
-
const path = String(args.path);
|
|
24
|
-
const resolved = safeResolvePath(ctx.baseDir, path);
|
|
25
|
-
// Get session-specific security config
|
|
26
|
-
const securityConfig = ctx.sessionContext
|
|
27
|
-
? getSessionSecurityConfig(ctx.config, ctx.sessionContext)
|
|
28
|
-
: ctx.config.security;
|
|
29
|
-
// Check file operations limit
|
|
30
|
-
const maxFileOps = securityConfig?.maxFileOperations ?? 100;
|
|
31
|
-
const currentCount = ctx.fileOperationsCount ?? 0;
|
|
32
|
-
if (currentCount >= maxFileOps) {
|
|
33
|
-
logSecurityBlock(ctx.sessionId, "file_write", `Maximum file operations (${maxFileOps}) exceeded`, path);
|
|
34
|
-
return {
|
|
35
|
-
success: false,
|
|
36
|
-
output: `[SECURITY BLOCKED] Maximum file operations (${maxFileOps}) exceeded`,
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
// Check path permissions
|
|
40
|
-
const scopeCheck = isPathWritable(ctx.baseDir, resolved, ctx.scope, ctx.config.security?.paths);
|
|
41
|
-
if (!scopeCheck.allowed) {
|
|
42
|
-
logSecurityBlock(ctx.sessionId, "file_write", scopeCheck.reason || "Path not allowed", path);
|
|
43
|
-
return {
|
|
44
|
-
success: false,
|
|
45
|
-
output: t("file.path_not_allowed", {
|
|
46
|
-
path: `${path} — ${scopeCheck.reason}`,
|
|
47
|
-
}),
|
|
48
|
-
};
|
|
49
|
-
}
|
|
50
|
-
const content = readFileSync(resolved, "utf-8");
|
|
51
|
-
const oldStr = String(args.old);
|
|
52
|
-
const newStr = String(args.new);
|
|
53
|
-
if (!content.includes(oldStr)) {
|
|
54
|
-
return {
|
|
55
|
-
success: false,
|
|
56
|
-
output: t("file.string_not_found", { str: oldStr }),
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
const updated = content.replace(oldStr, newStr);
|
|
60
|
-
// Check new content for dangerous patterns
|
|
61
|
-
const scanResult = scanContent(updated, path, ctx.config.security?.contentScan);
|
|
62
|
-
if (!scanResult.allowed) {
|
|
63
|
-
logSecurityBlock(ctx.sessionId, "file_write", scanResult.reason || "Content contains dangerous patterns", path);
|
|
64
|
-
return {
|
|
65
|
-
success: false,
|
|
66
|
-
output: `[SECURITY BLOCKED] ${scanResult.reason}`,
|
|
67
|
-
};
|
|
68
|
-
}
|
|
69
|
-
writeFileSync(resolved, updated, "utf-8");
|
|
70
|
-
const diff = generateDiff(content, updated);
|
|
71
|
-
// Increment file operations counter
|
|
72
|
-
ctx.fileOperationsCount = currentCount + 1;
|
|
73
|
-
// Log successful file edit
|
|
74
|
-
logFileWrite(ctx.sessionId, path, true, "File edited");
|
|
75
|
-
return { success: true, output: t("file.replaced_in", { path }), diff };
|
|
76
|
-
},
|
|
77
|
-
};
|