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
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from
|
|
2
|
-
import { homedir } from
|
|
3
|
-
import { join } from
|
|
4
|
-
import { readFileSync, writeFileSync, existsSync, copyFileSync } from
|
|
1
|
+
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'node:crypto';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, copyFileSync } from 'fs';
|
|
5
5
|
const DEFAULT_CONFIG = {
|
|
6
|
-
keyPath: join(homedir(),
|
|
7
|
-
algorithm:
|
|
6
|
+
keyPath: join(homedir(), '.mma', '.encryption-key'),
|
|
7
|
+
algorithm: 'aes-256-gcm',
|
|
8
8
|
};
|
|
9
9
|
const IV_LENGTH = 16;
|
|
10
10
|
const SALT_LENGTH = 16;
|
|
@@ -22,7 +22,7 @@ export function generateEncryptionKey() {
|
|
|
22
22
|
export function getOrCreateEncryptionKey(config) {
|
|
23
23
|
const { keyPath } = { ...DEFAULT_CONFIG, ...config };
|
|
24
24
|
// Ensure directory exists
|
|
25
|
-
const dir = join(keyPath,
|
|
25
|
+
const dir = join(keyPath, '..');
|
|
26
26
|
if (!existsSync(dir)) {
|
|
27
27
|
// Note: We can't use mkdirSync here because we're in a pure function
|
|
28
28
|
// This will be handled by the caller
|
|
@@ -30,7 +30,7 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
30
30
|
if (!existsSync(keyPath)) {
|
|
31
31
|
const key = generateEncryptionKey();
|
|
32
32
|
try {
|
|
33
|
-
writeFileSync(keyPath, key.toString(
|
|
33
|
+
writeFileSync(keyPath, key.toString('base64'), { mode: 0o600 });
|
|
34
34
|
return key;
|
|
35
35
|
}
|
|
36
36
|
catch {
|
|
@@ -40,8 +40,8 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
try {
|
|
43
|
-
const keyBase64 = readFileSync(keyPath,
|
|
44
|
-
return Buffer.from(keyBase64,
|
|
43
|
+
const keyBase64 = readFileSync(keyPath, 'utf8');
|
|
44
|
+
return Buffer.from(keyBase64, 'base64');
|
|
45
45
|
}
|
|
46
46
|
catch {
|
|
47
47
|
// Backup the corrupted key file before throwing
|
|
@@ -49,9 +49,7 @@ export function getOrCreateEncryptionKey(config) {
|
|
|
49
49
|
const backupPath = `${keyPath}.corrupted.${Date.now()}`;
|
|
50
50
|
copyFileSync(keyPath, backupPath);
|
|
51
51
|
}
|
|
52
|
-
catch {
|
|
53
|
-
/* backup failed, continue */
|
|
54
|
-
}
|
|
52
|
+
catch { /* backup failed, continue */ }
|
|
55
53
|
throw new Error(`Encryption key file is corrupted or unreadable: ${keyPath}. ` +
|
|
56
54
|
`All previously encrypted data will be unrecoverable. ` +
|
|
57
55
|
`Delete the key file to generate a new one.`);
|
|
@@ -70,63 +68,63 @@ export function encryptString(plaintext, key) {
|
|
|
70
68
|
const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
|
|
71
69
|
const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
|
|
72
70
|
// Encrypt with AES-256-CBC
|
|
73
|
-
const cipher = createCipheriv(
|
|
74
|
-
let encrypted = cipher.update(plaintext,
|
|
75
|
-
encrypted += cipher.final(
|
|
71
|
+
const cipher = createCipheriv('aes-256-cbc', encKey, iv);
|
|
72
|
+
let encrypted = cipher.update(plaintext, 'utf8', 'base64');
|
|
73
|
+
encrypted += cipher.final('base64');
|
|
76
74
|
// Create HMAC for authentication
|
|
77
|
-
const hmac = createHmac(
|
|
75
|
+
const hmac = createHmac('sha256', hmacKey);
|
|
78
76
|
hmac.update(iv);
|
|
79
|
-
hmac.update(Buffer.from(encrypted,
|
|
77
|
+
hmac.update(Buffer.from(encrypted, 'base64'));
|
|
80
78
|
const authTag = hmac.digest();
|
|
81
79
|
// Format: salt:iv:authTag:encrypted
|
|
82
|
-
return `${salt.toString(
|
|
80
|
+
return `${salt.toString('base64')}:${iv.toString('base64')}:${authTag.toString('base64')}:${encrypted}`;
|
|
83
81
|
}
|
|
84
82
|
/**
|
|
85
83
|
* Decrypt a string using AES-256-CBC + HMAC
|
|
86
84
|
*/
|
|
87
85
|
export function decryptString(encryptedText, key) {
|
|
88
|
-
const parts = encryptedText.split(
|
|
86
|
+
const parts = encryptedText.split(':');
|
|
89
87
|
if (parts.length !== 4) {
|
|
90
|
-
throw new Error(
|
|
88
|
+
throw new Error('Invalid encrypted format');
|
|
91
89
|
}
|
|
92
|
-
const salt = Buffer.from(parts[0],
|
|
93
|
-
const iv = Buffer.from(parts[1],
|
|
94
|
-
const authTag = Buffer.from(parts[2],
|
|
90
|
+
const salt = Buffer.from(parts[0], 'base64');
|
|
91
|
+
const iv = Buffer.from(parts[1], 'base64');
|
|
92
|
+
const authTag = Buffer.from(parts[2], 'base64');
|
|
95
93
|
const encrypted = parts[3];
|
|
96
94
|
// Derive encryption key and HMAC key from the main key
|
|
97
95
|
const encKey = scryptSync(key, salt, KEY_LENGTH);
|
|
98
96
|
const hmacSalt = Buffer.concat([salt, Buffer.from([1, 2, 3, 4])]);
|
|
99
97
|
const hmacKey = scryptSync(key, hmacSalt, KEY_LENGTH);
|
|
100
98
|
// Verify HMAC
|
|
101
|
-
const hmac = createHmac(
|
|
99
|
+
const hmac = createHmac('sha256', hmacKey);
|
|
102
100
|
hmac.update(iv);
|
|
103
|
-
hmac.update(Buffer.from(encrypted,
|
|
101
|
+
hmac.update(Buffer.from(encrypted, 'base64'));
|
|
104
102
|
const computedTag = hmac.digest();
|
|
105
103
|
if (!computedTag.equals(authTag)) {
|
|
106
|
-
throw new Error(
|
|
104
|
+
throw new Error('Authentication failed: invalid tag');
|
|
107
105
|
}
|
|
108
106
|
// Decrypt with AES-256-CBC
|
|
109
|
-
const decipher = createDecipheriv(
|
|
110
|
-
let decrypted = decipher.update(encrypted,
|
|
111
|
-
decrypted += decipher.final(
|
|
107
|
+
const decipher = createDecipheriv('aes-256-cbc', encKey, iv);
|
|
108
|
+
let decrypted = decipher.update(encrypted, 'base64', 'utf8');
|
|
109
|
+
decrypted += decipher.final('utf8');
|
|
112
110
|
return decrypted;
|
|
113
111
|
}
|
|
114
|
-
import { createHmac } from
|
|
112
|
+
import { createHmac } from 'node:crypto';
|
|
115
113
|
/**
|
|
116
114
|
* Check if a string looks like it's encrypted
|
|
117
115
|
*/
|
|
118
116
|
export function isEncryptedString(value) {
|
|
119
117
|
// Encrypted strings have the format: salt:iv:authTag:encrypted
|
|
120
118
|
// Each part is base64 encoded and separated by colons
|
|
121
|
-
const parts = value.split(
|
|
119
|
+
const parts = value.split(':');
|
|
122
120
|
if (parts.length !== 4)
|
|
123
121
|
return false;
|
|
124
122
|
// Check if all parts are valid base64
|
|
125
123
|
try {
|
|
126
|
-
Buffer.from(parts[0],
|
|
127
|
-
Buffer.from(parts[1],
|
|
128
|
-
Buffer.from(parts[2],
|
|
129
|
-
Buffer.from(parts[3],
|
|
124
|
+
Buffer.from(parts[0], 'base64');
|
|
125
|
+
Buffer.from(parts[1], 'base64');
|
|
126
|
+
Buffer.from(parts[2], 'base64');
|
|
127
|
+
Buffer.from(parts[3], 'base64');
|
|
130
128
|
return true;
|
|
131
129
|
}
|
|
132
130
|
catch {
|
|
@@ -137,28 +135,28 @@ export function isEncryptedString(value) {
|
|
|
137
135
|
* Sensitive field names that should be encrypted
|
|
138
136
|
*/
|
|
139
137
|
export const SENSITIVE_FIELD_NAMES = [
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
138
|
+
'api_key',
|
|
139
|
+
'apikey',
|
|
140
|
+
'apiKey',
|
|
141
|
+
'token',
|
|
142
|
+
'access_token',
|
|
143
|
+
'accessToken',
|
|
144
|
+
'password',
|
|
145
|
+
'passwd',
|
|
146
|
+
'pwd',
|
|
147
|
+
'secret',
|
|
148
|
+
'private_key',
|
|
149
|
+
'privateKey',
|
|
150
|
+
'auth',
|
|
151
|
+
'authorization',
|
|
152
|
+
'key',
|
|
155
153
|
];
|
|
156
154
|
/**
|
|
157
155
|
* Check if a field name indicates sensitive data
|
|
158
156
|
*/
|
|
159
157
|
export function isSensitiveFieldName(fieldName) {
|
|
160
158
|
const lowerFieldName = fieldName.toLowerCase();
|
|
161
|
-
return SENSITIVE_FIELD_NAMES.some(
|
|
159
|
+
return SENSITIVE_FIELD_NAMES.some(name => lowerFieldName.includes(name));
|
|
162
160
|
}
|
|
163
161
|
/**
|
|
164
162
|
* Encrypt sensitive fields in an object
|
|
@@ -166,12 +164,12 @@ export function isSensitiveFieldName(fieldName) {
|
|
|
166
164
|
export function encryptSensitiveFields(obj, key) {
|
|
167
165
|
const result = {};
|
|
168
166
|
for (const [fieldName, value] of Object.entries(obj)) {
|
|
169
|
-
if (isSensitiveFieldName(fieldName) && typeof value ===
|
|
167
|
+
if (isSensitiveFieldName(fieldName) && typeof value === 'string' && !isEncryptedString(value)) {
|
|
170
168
|
result[fieldName] = encryptString(value, key);
|
|
171
169
|
}
|
|
172
|
-
else if (typeof value ===
|
|
170
|
+
else if (typeof value === 'object' && value !== null) {
|
|
173
171
|
if (Array.isArray(value)) {
|
|
174
|
-
result[fieldName] = value.map(
|
|
172
|
+
result[fieldName] = value.map(item => typeof item === 'object' && item !== null && !(item instanceof RegExp)
|
|
175
173
|
? encryptSensitiveFields(item, key)
|
|
176
174
|
: item);
|
|
177
175
|
}
|
|
@@ -194,7 +192,7 @@ export function encryptSensitiveFields(obj, key) {
|
|
|
194
192
|
export function decryptSensitiveFields(obj, key) {
|
|
195
193
|
const result = {};
|
|
196
194
|
for (const [fieldName, value] of Object.entries(obj)) {
|
|
197
|
-
if (isSensitiveFieldName(fieldName) && typeof value ===
|
|
195
|
+
if (isSensitiveFieldName(fieldName) && typeof value === 'string' && isEncryptedString(value)) {
|
|
198
196
|
try {
|
|
199
197
|
result[fieldName] = decryptString(value, key);
|
|
200
198
|
}
|
|
@@ -203,9 +201,9 @@ export function decryptSensitiveFields(obj, key) {
|
|
|
203
201
|
result[fieldName] = value;
|
|
204
202
|
}
|
|
205
203
|
}
|
|
206
|
-
else if (typeof value ===
|
|
204
|
+
else if (typeof value === 'object' && value !== null) {
|
|
207
205
|
if (Array.isArray(value)) {
|
|
208
|
-
result[fieldName] = value.map(
|
|
206
|
+
result[fieldName] = value.map(item => typeof item === 'object' && item !== null && !(item instanceof RegExp)
|
|
209
207
|
? decryptSensitiveFields(item, key)
|
|
210
208
|
: item);
|
|
211
209
|
}
|
|
@@ -48,7 +48,15 @@ export function isUrlAllowed(url, securityConfig) {
|
|
|
48
48
|
export function sanitizeUrl(url) {
|
|
49
49
|
try {
|
|
50
50
|
const urlObj = new URL(url);
|
|
51
|
-
const sensitiveKeys = [
|
|
51
|
+
const sensitiveKeys = [
|
|
52
|
+
"api_key",
|
|
53
|
+
"token",
|
|
54
|
+
"password",
|
|
55
|
+
"secret",
|
|
56
|
+
"access_token",
|
|
57
|
+
"auth",
|
|
58
|
+
"key",
|
|
59
|
+
];
|
|
52
60
|
const sanitizedParams = new URLSearchParams(urlObj.search);
|
|
53
61
|
for (const key of sensitiveKeys) {
|
|
54
62
|
if (sanitizedParams.has(key)) {
|
|
@@ -199,7 +199,9 @@ function matchesGlobPattern(targetPath, baseDir, pattern) {
|
|
|
199
199
|
.replace(/\?/g, ".")
|
|
200
200
|
.replace(/\./g, "\\.");
|
|
201
201
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
202
|
-
const relativePath = resolvedTargetPath
|
|
202
|
+
const relativePath = resolvedTargetPath
|
|
203
|
+
.slice(resolvedBaseDir.length + 1)
|
|
204
|
+
.replace(/\\/g, "/");
|
|
203
205
|
return regex.test(relativePath);
|
|
204
206
|
}
|
|
205
207
|
// Exact match
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mergeSecurityConfig } from "../../config/security";
|
|
1
|
+
import { mergeSecurityConfig, } from "../../config/security";
|
|
2
2
|
/**
|
|
3
3
|
* Strict security policy - maximum security, minimum flexibility
|
|
4
4
|
* Recommended for: production environments, sensitive data, multi-user systems
|
|
@@ -70,7 +70,18 @@ export const STRICT_POLICY = {
|
|
|
70
70
|
"-R",
|
|
71
71
|
"--no-clobber",
|
|
72
72
|
],
|
|
73
|
-
dangerousOperators: [
|
|
73
|
+
dangerousOperators: [
|
|
74
|
+
">",
|
|
75
|
+
">>",
|
|
76
|
+
"2>",
|
|
77
|
+
"2>>",
|
|
78
|
+
"|",
|
|
79
|
+
"&&",
|
|
80
|
+
"||",
|
|
81
|
+
";",
|
|
82
|
+
"&",
|
|
83
|
+
"`",
|
|
84
|
+
],
|
|
74
85
|
logCommands: true,
|
|
75
86
|
},
|
|
76
87
|
paths: {
|
|
@@ -288,7 +299,12 @@ export const BALANCED_POLICY = {
|
|
|
288
299
|
auditNotifier: {
|
|
289
300
|
enabled: false,
|
|
290
301
|
minSeverity: "medium",
|
|
291
|
-
eventTypes: [
|
|
302
|
+
eventTypes: [
|
|
303
|
+
"security_block",
|
|
304
|
+
"bash_command",
|
|
305
|
+
"file_operation",
|
|
306
|
+
"network_request",
|
|
307
|
+
],
|
|
292
308
|
maxRetries: 3,
|
|
293
309
|
},
|
|
294
310
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync } from "fs";
|
|
1
|
+
import { readFileSync, writeFileSync, existsSync, readdirSync, unlinkSync, } from "fs";
|
|
2
2
|
import { join } from "path";
|
|
3
3
|
import { homedir } from "os";
|
|
4
4
|
import { encryptString, decryptString, isEncryptedString, ConfigEncryptor, } from "./encryption";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { join, resolve } from
|
|
2
|
-
import { homedir } from
|
|
3
|
-
import { mkdirSync, existsSync } from
|
|
4
|
-
import { DEFAULT_SECURITY_CONFIG } from
|
|
1
|
+
import { join, resolve } from 'path';
|
|
2
|
+
import { homedir } from 'os';
|
|
3
|
+
import { mkdirSync, existsSync } from 'fs';
|
|
4
|
+
import { DEFAULT_SECURITY_CONFIG } from '../../config/security';
|
|
5
5
|
/**
|
|
6
6
|
* Default session isolation configuration
|
|
7
7
|
*/
|
|
@@ -16,8 +16,8 @@ export const DEFAULT_SESSION_ISOLATION = {
|
|
|
16
16
|
export function createSessionContext(sessionId, projectDir, isolationConfig, securityOverrides) {
|
|
17
17
|
const config = { ...DEFAULT_SESSION_ISOLATION, ...isolationConfig };
|
|
18
18
|
// Create session-specific temporary directory
|
|
19
|
-
const baseDir = config.baseDir || join(homedir(),
|
|
20
|
-
const tempDir = join(baseDir,
|
|
19
|
+
const baseDir = config.baseDir || join(homedir(), '.mma', 'sessions', sessionId);
|
|
20
|
+
const tempDir = join(baseDir, 'temp');
|
|
21
21
|
if (config.isolateTempFiles && !existsSync(tempDir)) {
|
|
22
22
|
try {
|
|
23
23
|
mkdirSync(tempDir, { recursive: true, mode: 0o700 });
|
|
@@ -84,11 +84,11 @@ export function isPathInSessionScope(sessionContext, path) {
|
|
|
84
84
|
const workingDir = resolve(sessionContext.workingDir);
|
|
85
85
|
const tempDir = resolve(sessionContext.tempDir);
|
|
86
86
|
// Check if path is within working directory
|
|
87
|
-
if (resolvedPath.startsWith(workingDir +
|
|
87
|
+
if (resolvedPath.startsWith(workingDir + '/') || resolvedPath.startsWith(workingDir + '\\')) {
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
90
90
|
// Check if path is within temp directory
|
|
91
|
-
if (resolvedPath.startsWith(tempDir +
|
|
91
|
+
if (resolvedPath.startsWith(tempDir + '/') || resolvedPath.startsWith(tempDir + '\\')) {
|
|
92
92
|
return true;
|
|
93
93
|
}
|
|
94
94
|
return false;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { SessionStore } from
|
|
2
|
-
export { SessionManager } from
|
|
3
|
-
export { SessionModule } from
|
|
1
|
+
export { SessionStore } from './store';
|
|
2
|
+
export { SessionManager } from './manager';
|
|
3
|
+
export { SessionModule } from './module';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { t } from
|
|
1
|
+
import { t } from '../../i18n/index';
|
|
2
2
|
export class SessionModule {
|
|
3
|
-
name =
|
|
3
|
+
name = 'session';
|
|
4
4
|
manager;
|
|
5
5
|
constructor(manager) {
|
|
6
6
|
this.manager = manager;
|
|
@@ -10,15 +10,15 @@ export class SessionModule {
|
|
|
10
10
|
if (!meta || meta.messageCount === 0)
|
|
11
11
|
return null;
|
|
12
12
|
return {
|
|
13
|
-
content: t(
|
|
14
|
-
priority:
|
|
13
|
+
content: t('session.info', { name: meta.name, id: meta.id, count: meta.messageCount }),
|
|
14
|
+
priority: 'low',
|
|
15
15
|
essential: false,
|
|
16
16
|
estimatedTokens: 20,
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
19
|
getPlugin() {
|
|
20
20
|
return {
|
|
21
|
-
name:
|
|
21
|
+
name: 'session',
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
24
|
}
|
|
@@ -72,7 +72,9 @@ export class SessionStore {
|
|
|
72
72
|
return null;
|
|
73
73
|
try {
|
|
74
74
|
const raw = readFileSync(path, "utf-8");
|
|
75
|
-
const content = this.encryptor
|
|
75
|
+
const content = this.encryptor
|
|
76
|
+
? this.encryptor.decryptFileContent(raw)
|
|
77
|
+
: raw;
|
|
76
78
|
const meta = JSON.parse(content);
|
|
77
79
|
this._metaCache.set(id, meta);
|
|
78
80
|
return meta;
|
|
@@ -127,7 +129,9 @@ export class SessionStore {
|
|
|
127
129
|
.map(parseLine)
|
|
128
130
|
.filter((m) => m !== null);
|
|
129
131
|
}
|
|
130
|
-
return lines
|
|
132
|
+
return lines
|
|
133
|
+
.map(parseLine)
|
|
134
|
+
.filter((m) => m !== null);
|
|
131
135
|
}
|
|
132
136
|
catch {
|
|
133
137
|
return [];
|
|
@@ -173,7 +177,9 @@ export class SessionStore {
|
|
|
173
177
|
.map(parseLine)
|
|
174
178
|
.filter((e) => e !== null);
|
|
175
179
|
}
|
|
176
|
-
return lines
|
|
180
|
+
return lines
|
|
181
|
+
.map(parseLine)
|
|
182
|
+
.filter((e) => e !== null);
|
|
177
183
|
}
|
|
178
184
|
catch {
|
|
179
185
|
return [];
|
|
@@ -38,7 +38,8 @@ export class SkillsModule {
|
|
|
38
38
|
}
|
|
39
39
|
search(query) {
|
|
40
40
|
const q = query.toLowerCase();
|
|
41
|
-
return this.availableSkills.filter((s) => s.name.toLowerCase().includes(q) ||
|
|
41
|
+
return this.availableSkills.filter((s) => s.name.toLowerCase().includes(q) ||
|
|
42
|
+
s.description.toLowerCase().includes(q));
|
|
42
43
|
}
|
|
43
44
|
getBudget() {
|
|
44
45
|
return {
|
|
@@ -1,96 +1,32 @@
|
|
|
1
|
-
import { platform } from "os";
|
|
2
|
-
import { resolveSpawnCommand } from "../lsp/command";
|
|
3
|
-
/** Numeric semver comparison (x.y.z, prerelease ignored). */
|
|
4
|
-
export function semverGt(a, b) {
|
|
5
|
-
const pa = a
|
|
6
|
-
.replace(/^v/, "")
|
|
7
|
-
.split(/[.-]/)
|
|
8
|
-
.map((p) => Number.parseInt(p, 10) || 0);
|
|
9
|
-
const pb = b
|
|
10
|
-
.replace(/^v/, "")
|
|
11
|
-
.split(/[.-]/)
|
|
12
|
-
.map((p) => Number.parseInt(p, 10) || 0);
|
|
13
|
-
for (let i = 0; i < 3; i++) {
|
|
14
|
-
const na = pa[i] ?? 0;
|
|
15
|
-
const nb = pb[i] ?? 0;
|
|
16
|
-
if (na !== nb)
|
|
17
|
-
return na > nb;
|
|
18
|
-
}
|
|
19
|
-
return false;
|
|
20
|
-
}
|
|
21
|
-
const defaultRunner = async (command, args, options) => {
|
|
22
|
-
const { execFile } = await import("child_process");
|
|
23
|
-
return new Promise((resolve) => {
|
|
24
|
-
execFile(command, args, options, (err) => resolve({ error: err?.message }));
|
|
25
|
-
});
|
|
26
|
-
};
|
|
27
1
|
export class Updater {
|
|
28
2
|
currentVersion;
|
|
29
3
|
packageName;
|
|
30
|
-
|
|
31
|
-
constructor(currentVersion, packageName, runner) {
|
|
4
|
+
constructor(currentVersion, packageName) {
|
|
32
5
|
this.currentVersion = currentVersion;
|
|
33
6
|
this.packageName = packageName;
|
|
34
|
-
this.runner = runner ?? defaultRunner;
|
|
35
7
|
}
|
|
36
8
|
getCurrentVersion() {
|
|
37
9
|
return this.currentVersion;
|
|
38
10
|
}
|
|
39
|
-
getPackageName() {
|
|
40
|
-
return this.packageName;
|
|
41
|
-
}
|
|
42
11
|
async check() {
|
|
43
12
|
try {
|
|
44
13
|
const response = await fetch(`https://registry.npmjs.org/${this.packageName}`, {
|
|
45
|
-
headers: { Accept:
|
|
14
|
+
headers: { Accept: 'application/vnd.npm.install-v1+json' },
|
|
46
15
|
signal: AbortSignal.timeout(5000),
|
|
47
16
|
});
|
|
48
17
|
if (!response.ok) {
|
|
49
|
-
return {
|
|
50
|
-
updateAvailable: false,
|
|
51
|
-
current: this.currentVersion,
|
|
52
|
-
error: `HTTP ${response.status}`,
|
|
53
|
-
};
|
|
18
|
+
return { updateAvailable: false, current: this.currentVersion, error: `HTTP ${response.status}` };
|
|
54
19
|
}
|
|
55
20
|
const data = await response.json();
|
|
56
|
-
const latest = data[
|
|
21
|
+
const latest = data['dist-tags']?.latest;
|
|
57
22
|
if (!latest) {
|
|
58
|
-
return { updateAvailable: false, current: this.currentVersion, error:
|
|
23
|
+
return { updateAvailable: false, current: this.currentVersion, error: 'No latest tag' };
|
|
59
24
|
}
|
|
60
|
-
const updateAvailable =
|
|
25
|
+
const updateAvailable = latest !== this.currentVersion;
|
|
61
26
|
return { updateAvailable, current: this.currentVersion, latest };
|
|
62
27
|
}
|
|
63
28
|
catch (e) {
|
|
64
29
|
return { updateAvailable: false, current: this.currentVersion, error: e.message };
|
|
65
30
|
}
|
|
66
31
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Install the latest version globally via `npm install -g`.
|
|
69
|
-
* Returns an InstallResult (never throws).
|
|
70
|
-
*/
|
|
71
|
-
async install(latest) {
|
|
72
|
-
try {
|
|
73
|
-
const { command, args } = this.buildInstallCommand(latest);
|
|
74
|
-
const res = await this.runner(command, args, {
|
|
75
|
-
timeout: 120000,
|
|
76
|
-
windowsHide: true,
|
|
77
|
-
});
|
|
78
|
-
if (res.error)
|
|
79
|
-
return { success: false, error: res.error };
|
|
80
|
-
return { success: true, installed: latest };
|
|
81
|
-
}
|
|
82
|
-
catch (e) {
|
|
83
|
-
const msg = e?.message ?? String(e);
|
|
84
|
-
return { success: false, error: msg.split("\n")[0] };
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
buildInstallCommand(latest) {
|
|
88
|
-
const npmArgs = ["install", "-g", `${this.packageName}@${latest}`];
|
|
89
|
-
const command = resolveSpawnCommand("npm");
|
|
90
|
-
if (platform() === "win32" && /\.(cmd|bat)$/i.test(command)) {
|
|
91
|
-
// Node cannot spawn .cmd/.bat shims directly (spawn EINVAL) — route via cmd.exe.
|
|
92
|
-
return { command: "cmd.exe", args: ["/c", command, ...npmArgs] };
|
|
93
|
-
}
|
|
94
|
-
return { command, args: npmArgs };
|
|
95
|
-
}
|
|
96
32
|
}
|
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export { Updater } from
|
|
2
|
-
export { UpdaterModule } from "./module";
|
|
1
|
+
export { Updater } from './checker';
|
|
@@ -8,9 +8,9 @@ export class ProfileCompressor {
|
|
|
8
8
|
if (Object.keys(info.preferences).length > 0) {
|
|
9
9
|
const prefs = Object.entries(info.preferences)
|
|
10
10
|
.map(([k, v]) => `${k}=${v}`)
|
|
11
|
-
.join(
|
|
11
|
+
.join(',');
|
|
12
12
|
parts.push(`Prefs: ${prefs}`);
|
|
13
13
|
}
|
|
14
|
-
return parts.join(
|
|
14
|
+
return parts.join(', ');
|
|
15
15
|
}
|
|
16
16
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { UserProfile } from
|
|
1
|
+
export { UserProfile } from './profile';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from
|
|
2
|
-
import { join } from
|
|
3
|
-
import { homedir, hostname, platform, type } from
|
|
4
|
-
import { env } from
|
|
5
|
-
import { ProfileCompressor } from
|
|
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
6
|
export class UserProfile {
|
|
7
7
|
profileDir;
|
|
8
8
|
info = null;
|
|
@@ -15,7 +15,7 @@ export class UserProfile {
|
|
|
15
15
|
platform: platform(),
|
|
16
16
|
os: `${type()} ${hostname()}`,
|
|
17
17
|
hostname: hostname(),
|
|
18
|
-
shell: env.SHELL || env.ComSpec ||
|
|
18
|
+
shell: env.SHELL || env.ComSpec || 'unknown',
|
|
19
19
|
home: homedir(),
|
|
20
20
|
nodeVersion: process.version,
|
|
21
21
|
preferences: { ...this.preferences },
|
|
@@ -26,14 +26,14 @@ export class UserProfile {
|
|
|
26
26
|
if (!existsSync(this.profileDir)) {
|
|
27
27
|
mkdirSync(this.profileDir, { recursive: true });
|
|
28
28
|
}
|
|
29
|
-
writeFileSync(join(this.profileDir,
|
|
29
|
+
writeFileSync(join(this.profileDir, 'profile.json'), JSON.stringify({ ...this.info, preferences: this.preferences }, null, 2), 'utf-8');
|
|
30
30
|
}
|
|
31
31
|
load() {
|
|
32
|
-
const path = join(this.profileDir,
|
|
32
|
+
const path = join(this.profileDir, 'profile.json');
|
|
33
33
|
if (!existsSync(path))
|
|
34
34
|
return null;
|
|
35
35
|
try {
|
|
36
|
-
const data = JSON.parse(readFileSync(path,
|
|
36
|
+
const data = JSON.parse(readFileSync(path, 'utf-8'));
|
|
37
37
|
this.info = {
|
|
38
38
|
platform: data.platform,
|
|
39
39
|
os: data.os,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "fs";
|
|
2
2
|
import { resolve } from "path";
|
|
3
3
|
import { t } from "../i18n/index";
|
|
4
|
-
import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl
|
|
4
|
+
import { loadFileAsDataUrl, loadUrlAsDataUrl, readClipboardImage, bufferToDataUrl } from "../llm/image-utils";
|
|
5
5
|
import { logSecurityBlock } from "../modules/security/audit-log";
|
|
6
6
|
export const attachImageTool = {
|
|
7
7
|
name: "attach_image",
|