micro-models-agent 1.5.0 → 1.7.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/CHANGELOG.md +34 -0
- package/dist/i18n/en.json +3 -2
- package/dist/i18n/ru.json +3 -2
- package/dist/main.js +556 -323
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -2312,6 +2312,7 @@ var init_defaults = __esm(() => {
|
|
|
2312
2312
|
description: "browser + file reading for visual tasks"
|
|
2313
2313
|
}
|
|
2314
2314
|
},
|
|
2315
|
+
modelCapabilities: {},
|
|
2315
2316
|
contextWindow: 32768,
|
|
2316
2317
|
contextBudget: {
|
|
2317
2318
|
systemPrompt: 0.1,
|
|
@@ -2729,7 +2730,7 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2729
2730
|
"cli.config_key": "Config key (e.g., model, provider.baseUrl)",
|
|
2730
2731
|
"cli.set_value": "Set a config value",
|
|
2731
2732
|
"cli.set_done": "Set {key}={value}",
|
|
2732
|
-
"cli.show_config": "Show
|
|
2733
|
+
"cli.show_config": "Show the effective configuration (secrets redacted; use --show-secrets to reveal)",
|
|
2733
2734
|
"cli.migrate_config": "Migrate legacy config.json to domain format",
|
|
2734
2735
|
"cli.manage_models": "Manage models",
|
|
2735
2736
|
"cli.list_models": "List available models",
|
|
@@ -2804,6 +2805,7 @@ Fix the error and re-edit the file (a clean write clears the failure), or mark t
|
|
|
2804
2805
|
"cli.show_details": "Show session details",
|
|
2805
2806
|
"cli.delete_session": "Delete a session",
|
|
2806
2807
|
"cli.first_run": "First run detected. Running setup wizard...",
|
|
2808
|
+
"cli.first_run_oneshot": "No configuration found — running with defaults. Run `mma` (no arguments) once to set up the wizard.",
|
|
2807
2809
|
"cli.setup_saved_restart": "Settings saved. Restarting with the new configuration...",
|
|
2808
2810
|
"cli.unknown_cmd": "Unknown command: {name}.",
|
|
2809
2811
|
"cli.unknown_command": 'Unknown command "{input}". Did you mean "{suggestion}"?',
|
|
@@ -3028,7 +3030,7 @@ Excluded blocks: {count}`,
|
|
|
3028
3030
|
"setup.security_configure": " Configure security? (y/N)",
|
|
3029
3031
|
"setup.security_bash_block": " Block dangerous commands (rm, sudo, ssh, etc.)? (y/N)",
|
|
3030
3032
|
"setup.security_flags_block": " Block dangerous flags (--force, -rf, etc.)? (y/N)",
|
|
3031
|
-
"setup.
|
|
3033
|
+
"setup.security_paths_restrict": " Restrict access to .git/, .env, node_modules/? (Y/n)",
|
|
3032
3034
|
"skill.already_loaded": 'Skill "{name}" is already loaded',
|
|
3033
3035
|
"skill.not_found": 'Skill "{name}" not found in any source directory',
|
|
3034
3036
|
"skill.no_match": "No skills match for: {task}",
|
|
@@ -3605,7 +3607,7 @@ var init_ru = __esm(() => {
|
|
|
3605
3607
|
"cli.config_key": "Ключ конфига (напр., model, provider.baseUrl)",
|
|
3606
3608
|
"cli.set_value": "Установить значение",
|
|
3607
3609
|
"cli.set_done": "Установлено {key}={value}",
|
|
3608
|
-
"cli.show_config": "Показать
|
|
3610
|
+
"cli.show_config": "Показать действующую конфигурацию (секреты скрыты; используйте --show-secrets)",
|
|
3609
3611
|
"cli.migrate_config": "Мигрировать legacy config.json в domain-формат",
|
|
3610
3612
|
"cli.manage_models": "Управление моделями",
|
|
3611
3613
|
"cli.list_models": "Список доступных моделей",
|
|
@@ -3676,6 +3678,7 @@ var init_ru = __esm(() => {
|
|
|
3676
3678
|
"cli.show_details": "Показать детали сессии",
|
|
3677
3679
|
"cli.delete_session": "Удалить сессию",
|
|
3678
3680
|
"cli.first_run": "Первый запуск. Запускаем мастер настройки...",
|
|
3681
|
+
"cli.first_run_oneshot": "Конфигурация не найдена — запуск с настройками по умолчанию. Запустите `mma` без аргументов один раз для настройки.",
|
|
3679
3682
|
"cli.setup_saved_restart": "Настройки сохранены. Перезапуск с новой конфигурацией...",
|
|
3680
3683
|
"cli.unknown_cmd": "Неизвестная команда: {name}.",
|
|
3681
3684
|
"cli.unknown_command": 'Неизвестная команда "{input}". Возможно, вы имели в виду "{suggestion}"?',
|
|
@@ -3904,7 +3907,7 @@ var init_ru = __esm(() => {
|
|
|
3904
3907
|
"setup.security_configure": " Настроить безопасность? (y/N)",
|
|
3905
3908
|
"setup.security_bash_block": " Блокировать опасные команды (rm, sudo, ssh и т.д.)? (y/N)",
|
|
3906
3909
|
"setup.security_flags_block": " Блокировать опасные флаги (--force, -rf и т.д.)? (y/N)",
|
|
3907
|
-
"setup.
|
|
3910
|
+
"setup.security_paths_restrict": " Ограничить доступ к .git/, .env, node_modules/? (Y/n)",
|
|
3908
3911
|
"skill.already_loaded": 'Скилл "{name}" уже загружен',
|
|
3909
3912
|
"skill.not_found": 'Скилл "{name}" не найден ни в одном каталоге',
|
|
3910
3913
|
"skill.no_match": "Нет скиллов подходящих для: {task}",
|
|
@@ -5116,7 +5119,7 @@ var init_domains = __esm(() => {
|
|
|
5116
5119
|
"reasoning",
|
|
5117
5120
|
"hallucination"
|
|
5118
5121
|
],
|
|
5119
|
-
provider: ["provider", "orchestrator", "retry", "pricing"],
|
|
5122
|
+
provider: ["provider", "orchestrator", "retry", "pricing", "modelCapabilities"],
|
|
5120
5123
|
moe: ["moe", "experts"],
|
|
5121
5124
|
security: ["security"],
|
|
5122
5125
|
browser: ["browser"],
|
|
@@ -5324,9 +5327,6 @@ function loadConfig(options) {
|
|
|
5324
5327
|
config = deepMerge(config, domainConfig);
|
|
5325
5328
|
legacyDetected = false;
|
|
5326
5329
|
}
|
|
5327
|
-
if (!globalData && !domainConfig) {
|
|
5328
|
-
saveDomainFiles(config, options.configDir);
|
|
5329
|
-
}
|
|
5330
5330
|
const projectData = loadJSON(options.projectConfigPath);
|
|
5331
5331
|
if (projectData) {
|
|
5332
5332
|
config = deepMerge(config, projectData);
|
|
@@ -7548,7 +7548,7 @@ var init_factory = __esm(() => {
|
|
|
7548
7548
|
});
|
|
7549
7549
|
|
|
7550
7550
|
// src/llm/model-loader.ts
|
|
7551
|
-
async function
|
|
7551
|
+
async function probeModel(baseUrl, model, timeoutMs = 3000) {
|
|
7552
7552
|
const base = baseUrl.replace(/\/$/, "").replace(/\/v1\/?$/, "");
|
|
7553
7553
|
try {
|
|
7554
7554
|
const response = await fetch(`${base}/api/v1/models`, {
|
|
@@ -7562,10 +7562,18 @@ async function getLoadedContextLength(baseUrl, model, timeoutMs = 3000) {
|
|
|
7562
7562
|
if (!found)
|
|
7563
7563
|
return null;
|
|
7564
7564
|
const loadedInstance = (found.loaded_instances ?? [])[0];
|
|
7565
|
-
const
|
|
7566
|
-
if (typeof
|
|
7565
|
+
const contextLength = loadedInstance?.config?.context_length ?? found.max_context_length;
|
|
7566
|
+
if (typeof contextLength !== "number" || contextLength <= 0)
|
|
7567
7567
|
return null;
|
|
7568
|
-
|
|
7568
|
+
const raw = found.capabilities;
|
|
7569
|
+
let capabilities;
|
|
7570
|
+
if (raw && typeof raw.vision === "boolean") {
|
|
7571
|
+
capabilities = { vision: raw.vision };
|
|
7572
|
+
if (typeof raw.trained_for_tool_use === "boolean") {
|
|
7573
|
+
capabilities.tools = raw.trained_for_tool_use;
|
|
7574
|
+
}
|
|
7575
|
+
}
|
|
7576
|
+
return { model: found.key ?? found.name ?? model, contextLength, capabilities };
|
|
7569
7577
|
} catch {
|
|
7570
7578
|
return null;
|
|
7571
7579
|
}
|
|
@@ -15437,6 +15445,15 @@ var init_tool_output = __esm(() => {
|
|
|
15437
15445
|
init_i18n();
|
|
15438
15446
|
});
|
|
15439
15447
|
|
|
15448
|
+
// src/core/agent/tool-images.ts
|
|
15449
|
+
function queueToolImages(sink, images) {
|
|
15450
|
+
if (!sink || !images || images.length === 0)
|
|
15451
|
+
return;
|
|
15452
|
+
for (const image of images) {
|
|
15453
|
+
sink.addPendingImage(image);
|
|
15454
|
+
}
|
|
15455
|
+
}
|
|
15456
|
+
|
|
15440
15457
|
// src/core/agent/tool-batch.ts
|
|
15441
15458
|
class ToolBatchExecutor {
|
|
15442
15459
|
deps;
|
|
@@ -15566,6 +15583,7 @@ class ToolBatchExecutor {
|
|
|
15566
15583
|
arguments: call.arguments
|
|
15567
15584
|
});
|
|
15568
15585
|
answeredToolCallIds.add(call.id);
|
|
15586
|
+
queueToolImages(contextManager, result.images);
|
|
15569
15587
|
const tokensAfterTool = contextManager.getEstimatedTokens();
|
|
15570
15588
|
const costAfterTool = this.deps.costTracker.total;
|
|
15571
15589
|
onTool?.({
|
|
@@ -21291,6 +21309,119 @@ var init_session = __esm(() => {
|
|
|
21291
21309
|
init_i18n();
|
|
21292
21310
|
});
|
|
21293
21311
|
|
|
21312
|
+
// src/llm/image-utils.ts
|
|
21313
|
+
var exports_image_utils = {};
|
|
21314
|
+
__export(exports_image_utils, {
|
|
21315
|
+
readClipboardImage: () => readClipboardImage,
|
|
21316
|
+
loadUrlAsDataUrl: () => loadUrlAsDataUrl,
|
|
21317
|
+
loadFileAsDataUrl: () => loadFileAsDataUrl,
|
|
21318
|
+
detectMime: () => detectMime,
|
|
21319
|
+
bufferToDataUrl: () => bufferToDataUrl
|
|
21320
|
+
});
|
|
21321
|
+
import { readFileSync as readFileSync20 } from "fs";
|
|
21322
|
+
import { extname as extname4 } from "path";
|
|
21323
|
+
function detectMime(filePath) {
|
|
21324
|
+
const ext = extname4(filePath).toLowerCase();
|
|
21325
|
+
return MIME_MAP[ext] ?? "image/png";
|
|
21326
|
+
}
|
|
21327
|
+
async function readClipboardImage() {
|
|
21328
|
+
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21329
|
+
try {
|
|
21330
|
+
const img = Bun.Image.fromClipboard();
|
|
21331
|
+
if (img) {
|
|
21332
|
+
const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
|
|
21333
|
+
return Buffer.from(buf);
|
|
21334
|
+
}
|
|
21335
|
+
} catch {}
|
|
21336
|
+
}
|
|
21337
|
+
return readClipboardFallback();
|
|
21338
|
+
}
|
|
21339
|
+
async function readClipboardFallback() {
|
|
21340
|
+
const { platform: platform7 } = await import("os");
|
|
21341
|
+
const { execSync } = await import("child_process");
|
|
21342
|
+
const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
|
|
21343
|
+
const { join: join29 } = await import("path");
|
|
21344
|
+
if (platform7() !== "linux")
|
|
21345
|
+
return null;
|
|
21346
|
+
const tmpPath = join29(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
21347
|
+
const commands = [
|
|
21348
|
+
`wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
|
|
21349
|
+
`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
|
|
21350
|
+
];
|
|
21351
|
+
for (const cmd of commands) {
|
|
21352
|
+
try {
|
|
21353
|
+
execSync(cmd, { timeout: 5000 });
|
|
21354
|
+
const buf = readFileSync21(tmpPath);
|
|
21355
|
+
unlinkSync5(tmpPath);
|
|
21356
|
+
if (buf.length > 0)
|
|
21357
|
+
return buf;
|
|
21358
|
+
} catch {}
|
|
21359
|
+
}
|
|
21360
|
+
try {
|
|
21361
|
+
unlinkSync5(tmpPath);
|
|
21362
|
+
} catch {}
|
|
21363
|
+
return null;
|
|
21364
|
+
}
|
|
21365
|
+
async function loadFileAsDataUrl(filePath) {
|
|
21366
|
+
const buf = readFileSync20(filePath);
|
|
21367
|
+
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21368
|
+
try {
|
|
21369
|
+
const img = new Bun.Image(buf);
|
|
21370
|
+
const { width } = await img.metadata();
|
|
21371
|
+
const targetWidth = Math.min(800, width || 800);
|
|
21372
|
+
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21373
|
+
return { dataUrl };
|
|
21374
|
+
} catch {}
|
|
21375
|
+
}
|
|
21376
|
+
const b64 = buf.toString("base64");
|
|
21377
|
+
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
21378
|
+
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
21379
|
+
return { dataUrl: `data:${mime};base64,${b64}` };
|
|
21380
|
+
}
|
|
21381
|
+
async function loadUrlAsDataUrl(url) {
|
|
21382
|
+
const resp = await fetch(url);
|
|
21383
|
+
if (!resp.ok) {
|
|
21384
|
+
throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
|
|
21385
|
+
}
|
|
21386
|
+
const buf = Buffer.from(await resp.arrayBuffer());
|
|
21387
|
+
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21388
|
+
try {
|
|
21389
|
+
const img = new Bun.Image(buf);
|
|
21390
|
+
const { width } = await img.metadata();
|
|
21391
|
+
const targetWidth = Math.min(800, width || 800);
|
|
21392
|
+
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21393
|
+
return { dataUrl };
|
|
21394
|
+
} catch {}
|
|
21395
|
+
}
|
|
21396
|
+
const b64 = buf.toString("base64");
|
|
21397
|
+
return { dataUrl: `data:image/png;base64,${b64}` };
|
|
21398
|
+
}
|
|
21399
|
+
async function bufferToDataUrl(buf) {
|
|
21400
|
+
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21401
|
+
try {
|
|
21402
|
+
const img = new Bun.Image(buf);
|
|
21403
|
+
const { width } = await img.metadata();
|
|
21404
|
+
const targetWidth = Math.min(800, width || 800);
|
|
21405
|
+
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21406
|
+
return { dataUrl };
|
|
21407
|
+
} catch {}
|
|
21408
|
+
}
|
|
21409
|
+
const b64 = buf.toString("base64");
|
|
21410
|
+
return { dataUrl: `data:image/png;base64,${b64}` };
|
|
21411
|
+
}
|
|
21412
|
+
var MIME_MAP;
|
|
21413
|
+
var init_image_utils = __esm(() => {
|
|
21414
|
+
MIME_MAP = {
|
|
21415
|
+
".png": "image/png",
|
|
21416
|
+
".jpg": "image/jpeg",
|
|
21417
|
+
".jpeg": "image/jpeg",
|
|
21418
|
+
".gif": "image/gif",
|
|
21419
|
+
".webp": "image/webp",
|
|
21420
|
+
".bmp": "image/bmp",
|
|
21421
|
+
".svg": "image/svg+xml"
|
|
21422
|
+
};
|
|
21423
|
+
});
|
|
21424
|
+
|
|
21294
21425
|
// src/tools/browser.ts
|
|
21295
21426
|
import { join as join29 } from "path";
|
|
21296
21427
|
import { mkdirSync as mkdirSync16 } from "fs";
|
|
@@ -21327,18 +21458,9 @@ function formatScreenshotForTextModel(snapshot) {
|
|
|
21327
21458
|
|
|
21328
21459
|
${snapshot}`;
|
|
21329
21460
|
}
|
|
21330
|
-
function
|
|
21331
|
-
const
|
|
21332
|
-
return
|
|
21333
|
-
}
|
|
21334
|
-
function formatScreenshotResult(snapshot, screenshot, supportsVision) {
|
|
21335
|
-
if (!supportsVision) {
|
|
21336
|
-
return formatScreenshotForTextModel(snapshot);
|
|
21337
|
-
}
|
|
21338
|
-
const base642 = screenshot.toString("base64");
|
|
21339
|
-
return `${snapshot}
|
|
21340
|
-
|
|
21341
|
-
[Screenshot: data:image/png;base64,${base642}]`;
|
|
21461
|
+
async function screenshotToImagePart(screenshot) {
|
|
21462
|
+
const { dataUrl } = await bufferToDataUrl(screenshot);
|
|
21463
|
+
return { type: "image_url", image_url: { url: dataUrl } };
|
|
21342
21464
|
}
|
|
21343
21465
|
function createBrowserTool() {
|
|
21344
21466
|
return {
|
|
@@ -21403,12 +21525,15 @@ function createBrowserTool() {
|
|
|
21403
21525
|
}
|
|
21404
21526
|
const s = getSession(ctx);
|
|
21405
21527
|
const result = await serialize(() => s.execute(action, args));
|
|
21406
|
-
const supportsVision = modelSupportsVision(ctx.config.model);
|
|
21407
21528
|
if (result.screenshot) {
|
|
21408
|
-
|
|
21409
|
-
|
|
21410
|
-
|
|
21411
|
-
|
|
21529
|
+
if (ctx.capabilities?.vision) {
|
|
21530
|
+
return {
|
|
21531
|
+
success: result.success,
|
|
21532
|
+
output: result.output,
|
|
21533
|
+
images: [await screenshotToImagePart(result.screenshot)]
|
|
21534
|
+
};
|
|
21535
|
+
}
|
|
21536
|
+
return { success: result.success, output: formatScreenshotForTextModel(result.output) };
|
|
21412
21537
|
}
|
|
21413
21538
|
return { success: result.success, output: result.output };
|
|
21414
21539
|
}
|
|
@@ -21427,125 +21552,13 @@ var session = null, sessionFactory, actionChain;
|
|
|
21427
21552
|
var init_browser = __esm(() => {
|
|
21428
21553
|
init_session();
|
|
21429
21554
|
init_types();
|
|
21555
|
+
init_image_utils();
|
|
21430
21556
|
init_i18n();
|
|
21431
21557
|
init_output();
|
|
21432
21558
|
init_utils();
|
|
21433
21559
|
actionChain = Promise.resolve();
|
|
21434
21560
|
});
|
|
21435
21561
|
|
|
21436
|
-
// src/llm/image-utils.ts
|
|
21437
|
-
var exports_image_utils = {};
|
|
21438
|
-
__export(exports_image_utils, {
|
|
21439
|
-
readClipboardImage: () => readClipboardImage,
|
|
21440
|
-
loadUrlAsDataUrl: () => loadUrlAsDataUrl,
|
|
21441
|
-
loadFileAsDataUrl: () => loadFileAsDataUrl,
|
|
21442
|
-
detectMime: () => detectMime,
|
|
21443
|
-
bufferToDataUrl: () => bufferToDataUrl
|
|
21444
|
-
});
|
|
21445
|
-
import { readFileSync as readFileSync20 } from "fs";
|
|
21446
|
-
import { extname as extname4 } from "path";
|
|
21447
|
-
function detectMime(filePath) {
|
|
21448
|
-
const ext = extname4(filePath).toLowerCase();
|
|
21449
|
-
return MIME_MAP[ext] ?? "image/png";
|
|
21450
|
-
}
|
|
21451
|
-
async function readClipboardImage() {
|
|
21452
|
-
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21453
|
-
try {
|
|
21454
|
-
const img = Bun.Image.fromClipboard();
|
|
21455
|
-
if (img) {
|
|
21456
|
-
const buf = await img.resize(800, 800, { fit: "inside" }).jpeg({ quality: 60 }).buffer();
|
|
21457
|
-
return Buffer.from(buf);
|
|
21458
|
-
}
|
|
21459
|
-
} catch {}
|
|
21460
|
-
}
|
|
21461
|
-
return readClipboardFallback();
|
|
21462
|
-
}
|
|
21463
|
-
async function readClipboardFallback() {
|
|
21464
|
-
const { platform: platform7 } = await import("os");
|
|
21465
|
-
const { execSync } = await import("child_process");
|
|
21466
|
-
const { readFileSync: readFileSync21, unlinkSync: unlinkSync5 } = await import("fs");
|
|
21467
|
-
const { join: join30 } = await import("path");
|
|
21468
|
-
if (platform7() !== "linux")
|
|
21469
|
-
return null;
|
|
21470
|
-
const tmpPath = join30(process.env.TEMP || process.env.TMP || "/tmp", `mma-clip-${Date.now()}.png`);
|
|
21471
|
-
const commands = [
|
|
21472
|
-
`wl-paste --type image/png > "${tmpPath}" 2>/dev/null`,
|
|
21473
|
-
`xclip -selection clipboard -t image/png -o > "${tmpPath}" 2>/dev/null`
|
|
21474
|
-
];
|
|
21475
|
-
for (const cmd of commands) {
|
|
21476
|
-
try {
|
|
21477
|
-
execSync(cmd, { timeout: 5000 });
|
|
21478
|
-
const buf = readFileSync21(tmpPath);
|
|
21479
|
-
unlinkSync5(tmpPath);
|
|
21480
|
-
if (buf.length > 0)
|
|
21481
|
-
return buf;
|
|
21482
|
-
} catch {}
|
|
21483
|
-
}
|
|
21484
|
-
try {
|
|
21485
|
-
unlinkSync5(tmpPath);
|
|
21486
|
-
} catch {}
|
|
21487
|
-
return null;
|
|
21488
|
-
}
|
|
21489
|
-
async function loadFileAsDataUrl(filePath) {
|
|
21490
|
-
const buf = readFileSync20(filePath);
|
|
21491
|
-
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21492
|
-
try {
|
|
21493
|
-
const img = new Bun.Image(buf);
|
|
21494
|
-
const { width } = await img.metadata();
|
|
21495
|
-
const targetWidth = Math.min(800, width || 800);
|
|
21496
|
-
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21497
|
-
return { dataUrl };
|
|
21498
|
-
} catch {}
|
|
21499
|
-
}
|
|
21500
|
-
const b64 = buf.toString("base64");
|
|
21501
|
-
const ext = filePath.split(".").pop()?.toLowerCase();
|
|
21502
|
-
const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png";
|
|
21503
|
-
return { dataUrl: `data:${mime};base64,${b64}` };
|
|
21504
|
-
}
|
|
21505
|
-
async function loadUrlAsDataUrl(url) {
|
|
21506
|
-
const resp = await fetch(url);
|
|
21507
|
-
if (!resp.ok) {
|
|
21508
|
-
throw new Error(`Failed to fetch image: ${resp.status} ${resp.statusText}`);
|
|
21509
|
-
}
|
|
21510
|
-
const buf = Buffer.from(await resp.arrayBuffer());
|
|
21511
|
-
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21512
|
-
try {
|
|
21513
|
-
const img = new Bun.Image(buf);
|
|
21514
|
-
const { width } = await img.metadata();
|
|
21515
|
-
const targetWidth = Math.min(800, width || 800);
|
|
21516
|
-
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21517
|
-
return { dataUrl };
|
|
21518
|
-
} catch {}
|
|
21519
|
-
}
|
|
21520
|
-
const b64 = buf.toString("base64");
|
|
21521
|
-
return { dataUrl: `data:image/png;base64,${b64}` };
|
|
21522
|
-
}
|
|
21523
|
-
async function bufferToDataUrl(buf) {
|
|
21524
|
-
if (typeof Bun !== "undefined" && typeof Bun.Image !== "undefined") {
|
|
21525
|
-
try {
|
|
21526
|
-
const img = new Bun.Image(buf);
|
|
21527
|
-
const { width } = await img.metadata();
|
|
21528
|
-
const targetWidth = Math.min(800, width || 800);
|
|
21529
|
-
const dataUrl = await img.resize(targetWidth, 800, { fit: "inside" }).jpeg({ quality: 60 }).dataurl();
|
|
21530
|
-
return { dataUrl };
|
|
21531
|
-
} catch {}
|
|
21532
|
-
}
|
|
21533
|
-
const b64 = buf.toString("base64");
|
|
21534
|
-
return { dataUrl: `data:image/png;base64,${b64}` };
|
|
21535
|
-
}
|
|
21536
|
-
var MIME_MAP;
|
|
21537
|
-
var init_image_utils = __esm(() => {
|
|
21538
|
-
MIME_MAP = {
|
|
21539
|
-
".png": "image/png",
|
|
21540
|
-
".jpg": "image/jpeg",
|
|
21541
|
-
".jpeg": "image/jpeg",
|
|
21542
|
-
".gif": "image/gif",
|
|
21543
|
-
".webp": "image/webp",
|
|
21544
|
-
".bmp": "image/bmp",
|
|
21545
|
-
".svg": "image/svg+xml"
|
|
21546
|
-
};
|
|
21547
|
-
});
|
|
21548
|
-
|
|
21549
21562
|
// src/tools/attach-image.ts
|
|
21550
21563
|
import { existsSync as existsSync35 } from "fs";
|
|
21551
21564
|
import { resolve as resolve17 } from "path";
|
|
@@ -25292,7 +25305,7 @@ class BrowserModule {
|
|
|
25292
25305
|
"Workflow: open(url) → read snapshot → click/type on element numbers → read updated snapshot.",
|
|
25293
25306
|
'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.',
|
|
25294
25307
|
'Always start with "open" action. Use element numbers from snapshot for click/type targets.',
|
|
25295
|
-
'
|
|
25308
|
+
'The "screenshot" action is delivered automatically: a vision model receives the image, a text-only model receives the text snapshot instead. You do not need to decide — just call it and read what you get.'
|
|
25296
25309
|
].join(`
|
|
25297
25310
|
`),
|
|
25298
25311
|
priority: "normal",
|
|
@@ -27705,6 +27718,19 @@ var init_context_log = __esm(() => {
|
|
|
27705
27718
|
SEP2 = "─".repeat(78);
|
|
27706
27719
|
});
|
|
27707
27720
|
|
|
27721
|
+
// src/modules/providers/capabilities.ts
|
|
27722
|
+
function resolveModelCapabilities(preset, probe, override) {
|
|
27723
|
+
const base = preset ? { vision: preset.vision, tools: preset.tools } : { ...DEFAULT_MODEL_CAPABILITIES };
|
|
27724
|
+
return { ...base, ...probe ?? {}, ...override ?? {} };
|
|
27725
|
+
}
|
|
27726
|
+
var DEFAULT_MODEL_CAPABILITIES;
|
|
27727
|
+
var init_capabilities = __esm(() => {
|
|
27728
|
+
DEFAULT_MODEL_CAPABILITIES = {
|
|
27729
|
+
vision: false,
|
|
27730
|
+
tools: false
|
|
27731
|
+
};
|
|
27732
|
+
});
|
|
27733
|
+
|
|
27708
27734
|
// src/core/environment.ts
|
|
27709
27735
|
import { existsSync as existsSync50, readFileSync as readFileSync32, readdirSync as readdirSync16 } from "fs";
|
|
27710
27736
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
@@ -28336,7 +28362,9 @@ function buildSystemInfo(config, baseDir, profileCompressed) {
|
|
|
28336
28362
|
lines.push(`Runtime verification: when a bug is described in terms of runtime behavior (what happens when the app runs), reproduce it — run the app / build / browser tool — and confirm the fix against actual runtime output before calling "plan update step=N status=done". Check existing servers first with process_list/process_log: a stale dev server started BEFORE your change still runs the old pipeline and keeps failing even after the files on disk are fixed. Restart or kill it (process_kill) and verify the exact endpoint/port the user reported — a fresh server on a new port does NOT prove the user's error is gone.`);
|
|
28337
28363
|
const sec = config.security;
|
|
28338
28364
|
if (sec?.enabled) {
|
|
28339
|
-
const secLines = [
|
|
28365
|
+
const secLines = [
|
|
28366
|
+
`SECURITY RESTRICTIONS (enforced — blocked calls return "[SECURITY BLOCKED]", do not retry them):`
|
|
28367
|
+
];
|
|
28340
28368
|
if (sec.bash?.enabled && sec.bash.blacklist.length > 0) {
|
|
28341
28369
|
const cmds = sec.bash.blacklist.slice(0, 15).join(", ");
|
|
28342
28370
|
secLines.push(`Forbidden bash commands: ${cmds}${sec.bash.blacklist.length > 15 ? ", …" : ""}.`);
|
|
@@ -28418,19 +28446,23 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
|
|
|
28418
28446
|
logger4.info(t("env.model_loaded", { model: config.model, seconds: String(loadResult.loadTime) }));
|
|
28419
28447
|
}
|
|
28420
28448
|
}
|
|
28449
|
+
const capabilitiesRef = {};
|
|
28421
28450
|
const contextProbePromise = (async () => {
|
|
28422
28451
|
try {
|
|
28423
|
-
const probe = await
|
|
28452
|
+
const probe = await probeModel(config.provider.baseUrl, config.model);
|
|
28424
28453
|
if (!probe)
|
|
28425
28454
|
return null;
|
|
28426
|
-
if (probe.
|
|
28427
|
-
|
|
28428
|
-
|
|
28429
|
-
|
|
28455
|
+
if (probe.capabilities)
|
|
28456
|
+
capabilitiesRef.probe = probe.capabilities;
|
|
28457
|
+
const contextProbe = { model: probe.model, actual: probe.contextLength };
|
|
28458
|
+
if (probe.contextLength < config.contextWindow) {
|
|
28459
|
+
logger4.logSilent("warn", `Context probe: model "${probe.model}" is loaded with ${probe.contextLength} tokens, but contextWindow is configured as ${config.contextWindow} — overflow/429 risk. Lower contextWindow or reload the model with a larger context.`);
|
|
28460
|
+
} else if (probe.contextLength > config.contextWindow) {
|
|
28461
|
+
logger4.logSilent("info", `Context probe: model "${probe.model}" supports ${probe.contextLength} tokens — consider "mma context ${probe.contextLength}" to use the full window.`);
|
|
28430
28462
|
} else {
|
|
28431
|
-
logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.
|
|
28463
|
+
logger4.logSilent("debug", `Context probe: configured contextWindow matches loaded context (${probe.contextLength})`);
|
|
28432
28464
|
}
|
|
28433
|
-
return
|
|
28465
|
+
return contextProbe;
|
|
28434
28466
|
} catch {
|
|
28435
28467
|
return null;
|
|
28436
28468
|
}
|
|
@@ -28609,6 +28641,14 @@ async function bootstrap(configDir, projectDir, noAgentsMd, exitOnComplete, reas
|
|
|
28609
28641
|
get: () => sessionManager,
|
|
28610
28642
|
configurable: true
|
|
28611
28643
|
});
|
|
28644
|
+
Object.defineProperty(toolCtx, "capabilities", {
|
|
28645
|
+
get: () => {
|
|
28646
|
+
const cfg = configRef.current;
|
|
28647
|
+
const spec = BUILTIN_PROVIDERS.find((p) => p.type === cfg.provider.type);
|
|
28648
|
+
return resolveModelCapabilities(spec?.capabilities, capabilitiesRef.probe, cfg.modelCapabilities?.[cfg.model]);
|
|
28649
|
+
},
|
|
28650
|
+
configurable: true
|
|
28651
|
+
});
|
|
28612
28652
|
const toolExecutor = new ToolExecutor(toolRegistry, toolCtx, pluginManager);
|
|
28613
28653
|
toolCtx.llmProvider = llmProvider;
|
|
28614
28654
|
toolCtx.toolExecutor = toolExecutor;
|
|
@@ -28832,6 +28872,7 @@ var init_bootstrap = __esm(() => {
|
|
|
28832
28872
|
init_i18n();
|
|
28833
28873
|
init_agent();
|
|
28834
28874
|
init_context_log();
|
|
28875
|
+
init_capabilities();
|
|
28835
28876
|
init_version();
|
|
28836
28877
|
init_environment();
|
|
28837
28878
|
init_utils();
|
|
@@ -29592,11 +29633,12 @@ async function runSetup(externalRl) {
|
|
|
29592
29633
|
const configureSecurity = await ask(rl, t("setup.security_configure"), "n");
|
|
29593
29634
|
let securityBashBlock = false;
|
|
29594
29635
|
let securityFlagsBlock = false;
|
|
29595
|
-
let
|
|
29636
|
+
let securityPathsRestrict = false;
|
|
29596
29637
|
if (configureSecurity.toLowerCase() === "y" || configureSecurity.toLowerCase() === "yes") {
|
|
29597
29638
|
securityBashBlock = (await ask(rl, t("setup.security_bash_block"), "n")).toLowerCase() === "y";
|
|
29598
29639
|
securityFlagsBlock = (await ask(rl, t("setup.security_flags_block"), "n")).toLowerCase() === "y";
|
|
29599
|
-
|
|
29640
|
+
const pathsAnswer = (await ask(rl, t("setup.security_paths_restrict"), "Y")).toLowerCase();
|
|
29641
|
+
securityPathsRestrict = pathsAnswer !== "n" && pathsAnswer !== "no";
|
|
29600
29642
|
}
|
|
29601
29643
|
if (ownRl) {
|
|
29602
29644
|
rl.close();
|
|
@@ -29611,7 +29653,7 @@ async function runSetup(externalRl) {
|
|
|
29611
29653
|
locale,
|
|
29612
29654
|
securityBashBlock,
|
|
29613
29655
|
securityFlagsBlock,
|
|
29614
|
-
|
|
29656
|
+
securityPathsRestrict
|
|
29615
29657
|
};
|
|
29616
29658
|
getDefaultChannel().writeLine(pc.green(pc.bold(t("setup.complete"))));
|
|
29617
29659
|
const summary = [
|
|
@@ -29652,6 +29694,73 @@ var init_setup = __esm(() => {
|
|
|
29652
29694
|
];
|
|
29653
29695
|
});
|
|
29654
29696
|
|
|
29697
|
+
// src/cli/setup-order.ts
|
|
29698
|
+
function applyAnswersToConfig(config, a) {
|
|
29699
|
+
config.provider = {
|
|
29700
|
+
...config.provider,
|
|
29701
|
+
type: a.provider,
|
|
29702
|
+
baseUrl: a.apiBase,
|
|
29703
|
+
apiKey: a.apiKey
|
|
29704
|
+
};
|
|
29705
|
+
config.model = a.model;
|
|
29706
|
+
config.contextWindow = a.contextWindow;
|
|
29707
|
+
config.maxToolIterations = a.maxToolIterations;
|
|
29708
|
+
config.locale = a.locale;
|
|
29709
|
+
if ((config.provider.entries?.length ?? 0) > 0) {
|
|
29710
|
+
const entries = config.provider.entries ?? [];
|
|
29711
|
+
const activeName = config.provider.active || entries[0]?.label || entries[0]?.type;
|
|
29712
|
+
const idx = entries.findIndex((e) => e.label === activeName || e.type === activeName);
|
|
29713
|
+
const target = idx >= 0 ? idx : 0;
|
|
29714
|
+
const current = entries[target];
|
|
29715
|
+
const keepLabel = Boolean(current.label && current.label !== current.type);
|
|
29716
|
+
const updated = {
|
|
29717
|
+
...current,
|
|
29718
|
+
type: a.provider,
|
|
29719
|
+
baseUrl: a.apiBase,
|
|
29720
|
+
apiKey: a.apiKey,
|
|
29721
|
+
contextWindow: a.contextWindow,
|
|
29722
|
+
...keepLabel ? {} : { label: a.provider }
|
|
29723
|
+
};
|
|
29724
|
+
const next = entries.slice();
|
|
29725
|
+
next[target] = updated;
|
|
29726
|
+
config.provider.entries = next;
|
|
29727
|
+
config.provider.active = updated.label || updated.type;
|
|
29728
|
+
}
|
|
29729
|
+
if (config.security) {
|
|
29730
|
+
config.security.enabled = true;
|
|
29731
|
+
config.security.bash.enabled = true;
|
|
29732
|
+
config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
29733
|
+
if (a.securityBashBlock) {
|
|
29734
|
+
config.security.bash.blacklist = [
|
|
29735
|
+
...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
|
|
29736
|
+
];
|
|
29737
|
+
}
|
|
29738
|
+
config.security.paths.enabled = a.securityPathsRestrict;
|
|
29739
|
+
}
|
|
29740
|
+
return config;
|
|
29741
|
+
}
|
|
29742
|
+
var SECURITY_BASH_BLOCKLIST;
|
|
29743
|
+
var init_setup_order = __esm(() => {
|
|
29744
|
+
SECURITY_BASH_BLOCKLIST = [
|
|
29745
|
+
"rm",
|
|
29746
|
+
"dd",
|
|
29747
|
+
"chmod",
|
|
29748
|
+
"wget",
|
|
29749
|
+
"curl",
|
|
29750
|
+
"scp",
|
|
29751
|
+
"ssh",
|
|
29752
|
+
"nc",
|
|
29753
|
+
"netcat",
|
|
29754
|
+
"sudo",
|
|
29755
|
+
"su",
|
|
29756
|
+
"kill",
|
|
29757
|
+
"pkill",
|
|
29758
|
+
"killall",
|
|
29759
|
+
"shutdown",
|
|
29760
|
+
"reboot"
|
|
29761
|
+
];
|
|
29762
|
+
});
|
|
29763
|
+
|
|
29655
29764
|
// src/modules/indexer/map-command.ts
|
|
29656
29765
|
async function runMapAction(module, action, query) {
|
|
29657
29766
|
const summary = async () => {
|
|
@@ -37947,7 +38056,7 @@ __export(exports_repl_commands, {
|
|
|
37947
38056
|
registerAllCommands: () => registerAllCommands,
|
|
37948
38057
|
COMMAND_GROUPS: () => COMMAND_GROUPS
|
|
37949
38058
|
});
|
|
37950
|
-
import { join as
|
|
38059
|
+
import { join as join55, dirname as dirname25 } from "path";
|
|
37951
38060
|
import { existsSync as existsSync60 } from "fs";
|
|
37952
38061
|
function registerAllCommands(ctx) {
|
|
37953
38062
|
registerBuiltinCommands(ctx);
|
|
@@ -38148,14 +38257,8 @@ function registerConfigCommands(ctx) {
|
|
|
38148
38257
|
ctx.output.writeLine(pc.yellow(t("repl.wizard_running")));
|
|
38149
38258
|
await ctx.withExclusiveInput(async () => {
|
|
38150
38259
|
const answers = await runSetup(ctx.rl);
|
|
38151
|
-
const configPath =
|
|
38152
|
-
ctx.config
|
|
38153
|
-
ctx.config.provider.baseUrl = answers.apiBase;
|
|
38154
|
-
ctx.config.provider.apiKey = answers.apiKey;
|
|
38155
|
-
ctx.config.model = answers.model;
|
|
38156
|
-
ctx.config.contextWindow = answers.contextWindow;
|
|
38157
|
-
ctx.config.maxToolIterations = answers.maxToolIterations;
|
|
38158
|
-
ctx.config.locale = answers.locale;
|
|
38260
|
+
const configPath = join55(ctx.configDir, "config.json");
|
|
38261
|
+
applyAnswersToConfig(ctx.config, answers);
|
|
38159
38262
|
saveConfig(ctx.config, configPath, dirname25(configPath));
|
|
38160
38263
|
await ctx.agent.reconfigure(ctx.config);
|
|
38161
38264
|
ctx.output.writeLine(pc.green(t("cli.config_saved")));
|
|
@@ -38279,7 +38382,7 @@ function registerProviderCommands(ctx) {
|
|
|
38279
38382
|
});
|
|
38280
38383
|
ctx.config.provider.entries = entries;
|
|
38281
38384
|
ctx.config.provider.active = ctx.config.provider.active || ctx.config.provider.type;
|
|
38282
|
-
const configPath =
|
|
38385
|
+
const configPath = join55(ctx.configDir, "config.json");
|
|
38283
38386
|
saveConfig(ctx.config, configPath, dirname25(configPath));
|
|
38284
38387
|
await ctx.agent.reconfigure(ctx.config);
|
|
38285
38388
|
ctx.output.writeLine(pc.green(t("cli.provider_added", { name })));
|
|
@@ -38346,7 +38449,7 @@ function registerProviderCommands(ctx) {
|
|
|
38346
38449
|
return;
|
|
38347
38450
|
}
|
|
38348
38451
|
ctx.config.model = name;
|
|
38349
|
-
const configPath =
|
|
38452
|
+
const configPath = join55(ctx.configDir, "config.json");
|
|
38350
38453
|
saveConfig(ctx.config, configPath, dirname25(configPath));
|
|
38351
38454
|
await ctx.agent.reconfigure(ctx.config);
|
|
38352
38455
|
ctx.output.writeLine(pc.green(t("repl.model_set", { name })));
|
|
@@ -38375,7 +38478,7 @@ function registerProviderCommands(ctx) {
|
|
|
38375
38478
|
usage: t("repl.context_usage"),
|
|
38376
38479
|
action: async (args) => {
|
|
38377
38480
|
const save = async () => {
|
|
38378
|
-
const configPath =
|
|
38481
|
+
const configPath = join55(ctx.configDir, "config.json");
|
|
38379
38482
|
saveConfig(ctx.config, configPath, dirname25(configPath));
|
|
38380
38483
|
await ctx.agent.reconfigure(ctx.config);
|
|
38381
38484
|
};
|
|
@@ -38768,11 +38871,11 @@ function registerSkillCommands(ctx) {
|
|
|
38768
38871
|
});
|
|
38769
38872
|
}
|
|
38770
38873
|
async function runConfigMigrate(ctx) {
|
|
38771
|
-
const { hasDomainFiles:
|
|
38874
|
+
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
38772
38875
|
const { loadConfig: loadCfg } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
38773
38876
|
const configDir = ctx.configDir;
|
|
38774
|
-
const configPath =
|
|
38775
|
-
if (
|
|
38877
|
+
const configPath = join55(configDir, "config.json");
|
|
38878
|
+
if (hasDomainFiles2(configDir)) {
|
|
38776
38879
|
ctx.output.writeLine(pc.yellow(t("config.migrate_no_legacy")));
|
|
38777
38880
|
return;
|
|
38778
38881
|
}
|
|
@@ -38781,11 +38884,11 @@ async function runConfigMigrate(ctx) {
|
|
|
38781
38884
|
return;
|
|
38782
38885
|
}
|
|
38783
38886
|
ctx.output.writeLine(t("config.migrate_start"));
|
|
38784
|
-
const { config } = loadCfg({ configDir, projectConfigPath:
|
|
38887
|
+
const { config } = loadCfg({ configDir, projectConfigPath: join55(configDir, ".mmrc") });
|
|
38785
38888
|
saveConfig(config, configPath, configDir);
|
|
38786
|
-
const { renameSync: renameSync4, readdirSync:
|
|
38889
|
+
const { renameSync: renameSync4, readdirSync: readdirSync20 } = await import("fs");
|
|
38787
38890
|
renameSync4(configPath, configPath + ".bak");
|
|
38788
|
-
const domainFiles =
|
|
38891
|
+
const domainFiles = readdirSync20(join55(configDir, "config")).filter((f) => f.endsWith(".json"));
|
|
38789
38892
|
ctx.output.writeLine(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
|
|
38790
38893
|
}
|
|
38791
38894
|
var version2, COMMAND_GROUPS;
|
|
@@ -38795,6 +38898,7 @@ var init_repl_commands = __esm(() => {
|
|
|
38795
38898
|
init_table();
|
|
38796
38899
|
init_i18n();
|
|
38797
38900
|
init_setup();
|
|
38901
|
+
init_setup_order();
|
|
38798
38902
|
init_config2();
|
|
38799
38903
|
init_presets();
|
|
38800
38904
|
init_budget();
|
|
@@ -38850,6 +38954,7 @@ var {
|
|
|
38850
38954
|
init_bootstrap();
|
|
38851
38955
|
init_config2();
|
|
38852
38956
|
init_setup();
|
|
38957
|
+
init_setup_order();
|
|
38853
38958
|
init_i18n();
|
|
38854
38959
|
init_colors();
|
|
38855
38960
|
import { join as join53, dirname as dirname24 } from "path";
|
|
@@ -39363,7 +39468,7 @@ function createSecurityCommand(program2) {
|
|
|
39363
39468
|
const auditNotifier = security.auditNotifier || {};
|
|
39364
39469
|
getDefaultChannel().writeLine(t("cli.security.current_policy"));
|
|
39365
39470
|
getDefaultChannel().writeLine(` ${t("cli.security.bash_enabled")}: ${bash.blacklist?.length > 0 ? t("cli.yes") : t("cli.no")}`);
|
|
39366
|
-
getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.
|
|
39471
|
+
getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
39367
39472
|
getDefaultChannel().writeLine(` ${t("cli.security.network_validation")}: ${network.deniedDomains?.length > 0 || network.allowedDomains?.length > 0 ? t("cli.yes") : t("cli.no")}`);
|
|
39368
39473
|
getDefaultChannel().writeLine(` ${t("cli.security.content_scanning")}: ${contentScan.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
39369
39474
|
getDefaultChannel().writeLine(` ${t("cli.security.max_recursion")}: ${security.maxRecursionDepth ?? 3}`);
|
|
@@ -39671,50 +39776,60 @@ function extractLatestChangelog(changelog) {
|
|
|
39671
39776
|
|
|
39672
39777
|
// src/cli/commands.ts
|
|
39673
39778
|
init_output();
|
|
39779
|
+
|
|
39780
|
+
// src/config/redact.ts
|
|
39781
|
+
var SECRET_KEYS = new Set([
|
|
39782
|
+
"apiKey",
|
|
39783
|
+
"api_key",
|
|
39784
|
+
"apikey",
|
|
39785
|
+
"token",
|
|
39786
|
+
"tokens",
|
|
39787
|
+
"secret",
|
|
39788
|
+
"password",
|
|
39789
|
+
"authorization",
|
|
39790
|
+
"webhookUrl"
|
|
39791
|
+
]);
|
|
39792
|
+
var SECRET_MAPS = new Set(["headers", "env"]);
|
|
39793
|
+
var MASK = "***";
|
|
39794
|
+
function walk(value, parentKey, depth = 0) {
|
|
39795
|
+
if (depth > 100)
|
|
39796
|
+
return value;
|
|
39797
|
+
if (typeof value === "string") {
|
|
39798
|
+
return parentKey && SECRET_KEYS.has(parentKey) ? MASK : value;
|
|
39799
|
+
}
|
|
39800
|
+
if (Array.isArray(value)) {
|
|
39801
|
+
return value.map((v) => walk(v, parentKey, depth + 1));
|
|
39802
|
+
}
|
|
39803
|
+
if (value && typeof value === "object" && !(value instanceof RegExp)) {
|
|
39804
|
+
if (parentKey && SECRET_MAPS.has(parentKey)) {
|
|
39805
|
+
const masked = {};
|
|
39806
|
+
for (const k of Object.keys(value))
|
|
39807
|
+
masked[k] = MASK;
|
|
39808
|
+
return masked;
|
|
39809
|
+
}
|
|
39810
|
+
const out = {};
|
|
39811
|
+
for (const [k, v] of Object.entries(value)) {
|
|
39812
|
+
out[k] = walk(v, k, depth + 1);
|
|
39813
|
+
}
|
|
39814
|
+
return out;
|
|
39815
|
+
}
|
|
39816
|
+
return value;
|
|
39817
|
+
}
|
|
39818
|
+
function redactConfigSecrets(config) {
|
|
39819
|
+
return walk(config);
|
|
39820
|
+
}
|
|
39821
|
+
|
|
39822
|
+
// src/cli/commands.ts
|
|
39674
39823
|
var version = readMmaVersion();
|
|
39824
|
+
function configForDisplay(config, showSecrets) {
|
|
39825
|
+
return showSecrets ? config : redactConfigSecrets(config);
|
|
39826
|
+
}
|
|
39675
39827
|
function buildInitCommand(program2) {
|
|
39676
39828
|
program2.command("init").description(t("cli.init")).action(async () => {
|
|
39677
39829
|
const answers = await runSetup();
|
|
39678
39830
|
const configPath = join53(homedir17(), ".mma", "config.json");
|
|
39679
39831
|
const { config } = await bootstrap();
|
|
39680
|
-
config
|
|
39681
|
-
config.provider.baseUrl = answers.apiBase;
|
|
39682
|
-
config.provider.apiKey = answers.apiKey;
|
|
39683
|
-
config.model = answers.model;
|
|
39684
|
-
config.contextWindow = answers.contextWindow;
|
|
39685
|
-
config.maxToolIterations = answers.maxToolIterations;
|
|
39686
|
-
config.locale = answers.locale;
|
|
39687
|
-
if (config.security) {
|
|
39688
|
-
config.security.enabled = true;
|
|
39689
|
-
config.security.bash.enabled = true;
|
|
39690
|
-
config.security.bash.blockDangerousFlags = answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
39691
|
-
if (answers.securityBashBlock) {
|
|
39692
|
-
config.security.bash.blacklist = [
|
|
39693
|
-
...new Set([
|
|
39694
|
-
...config.security.bash.blacklist,
|
|
39695
|
-
"rm",
|
|
39696
|
-
"dd",
|
|
39697
|
-
"chmod",
|
|
39698
|
-
"wget",
|
|
39699
|
-
"curl",
|
|
39700
|
-
"scp",
|
|
39701
|
-
"ssh",
|
|
39702
|
-
"nc",
|
|
39703
|
-
"netcat",
|
|
39704
|
-
"sudo",
|
|
39705
|
-
"su",
|
|
39706
|
-
"kill",
|
|
39707
|
-
"pkill",
|
|
39708
|
-
"killall",
|
|
39709
|
-
"shutdown",
|
|
39710
|
-
"reboot"
|
|
39711
|
-
])
|
|
39712
|
-
];
|
|
39713
|
-
}
|
|
39714
|
-
if (!answers.securityPathsDeny) {
|
|
39715
|
-
config.security.paths.denied = [];
|
|
39716
|
-
}
|
|
39717
|
-
}
|
|
39832
|
+
applyAnswersToConfig(config, answers);
|
|
39718
39833
|
saveConfig(config, configPath, dirname24(configPath));
|
|
39719
39834
|
getDefaultChannel().writeLine(t("cli.config_saved"));
|
|
39720
39835
|
});
|
|
@@ -39745,16 +39860,16 @@ function buildConfigCommands(program2) {
|
|
|
39745
39860
|
saveConfig(config, configPath, dirname24(configPath));
|
|
39746
39861
|
getDefaultChannel().writeLine(t("cli.set_done", { key, value }));
|
|
39747
39862
|
});
|
|
39748
|
-
configCmd.command("show").description(t("cli.show_config")).action(async () => {
|
|
39863
|
+
configCmd.command("show").description(t("cli.show_config")).option("--show-secrets", "Print secrets (API keys, tokens) unredacted").action(async (opts) => {
|
|
39749
39864
|
const { config } = await bootstrap();
|
|
39750
|
-
writeMachineJson(config);
|
|
39865
|
+
writeMachineJson(configForDisplay(config, opts.showSecrets));
|
|
39751
39866
|
});
|
|
39752
39867
|
configCmd.command("migrate").description(t("cli.migrate_config")).action(async () => {
|
|
39753
39868
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
39754
|
-
const { hasDomainFiles:
|
|
39869
|
+
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
39755
39870
|
const configDir = join53(homedir17(), ".mma");
|
|
39756
39871
|
const configPath = join53(configDir, "config.json");
|
|
39757
|
-
if (
|
|
39872
|
+
if (hasDomainFiles2(configDir)) {
|
|
39758
39873
|
getDefaultChannel().writeLine(pc.yellow(t("config.migrate_no_legacy")));
|
|
39759
39874
|
return;
|
|
39760
39875
|
}
|
|
@@ -40176,6 +40291,7 @@ import * as readline2 from "readline";
|
|
|
40176
40291
|
|
|
40177
40292
|
// src/ui/line-editor.ts
|
|
40178
40293
|
init_string_width();
|
|
40294
|
+
init_colors();
|
|
40179
40295
|
import * as readline from "readline";
|
|
40180
40296
|
|
|
40181
40297
|
// src/ui/line-math.ts
|
|
@@ -40288,6 +40404,7 @@ class LineEditor {
|
|
|
40288
40404
|
pasteLastWasCR = false;
|
|
40289
40405
|
lastWasCR = false;
|
|
40290
40406
|
questionCb = null;
|
|
40407
|
+
completionSession = null;
|
|
40291
40408
|
rawMode = false;
|
|
40292
40409
|
listeners = {};
|
|
40293
40410
|
dsrDataHandler;
|
|
@@ -40421,6 +40538,7 @@ class LineEditor {
|
|
|
40421
40538
|
this.col = 0;
|
|
40422
40539
|
this.historyIndex = -1;
|
|
40423
40540
|
this.stash = null;
|
|
40541
|
+
this.completionSession = null;
|
|
40424
40542
|
this.invalidateAnchor();
|
|
40425
40543
|
if (this.input.isTTY)
|
|
40426
40544
|
this.render();
|
|
@@ -40431,6 +40549,7 @@ class LineEditor {
|
|
|
40431
40549
|
this.lines = [""];
|
|
40432
40550
|
this.row = 0;
|
|
40433
40551
|
this.col = 0;
|
|
40552
|
+
this.completionSession = null;
|
|
40434
40553
|
this.invalidateAnchor();
|
|
40435
40554
|
if (this.input.isTTY)
|
|
40436
40555
|
this.render();
|
|
@@ -40496,6 +40615,27 @@ class LineEditor {
|
|
|
40496
40615
|
}
|
|
40497
40616
|
return;
|
|
40498
40617
|
}
|
|
40618
|
+
if (this.completionSession) {
|
|
40619
|
+
const ck = key;
|
|
40620
|
+
const plain = !ck.ctrl && !ck.meta;
|
|
40621
|
+
if (ck?.name === "down" && plain) {
|
|
40622
|
+
this.cycleCompletion(1);
|
|
40623
|
+
return;
|
|
40624
|
+
}
|
|
40625
|
+
if (ck?.name === "up" && plain) {
|
|
40626
|
+
this.cycleCompletion(-1);
|
|
40627
|
+
return;
|
|
40628
|
+
}
|
|
40629
|
+
if (ck?.name === "tab") {
|
|
40630
|
+
this.cycleCompletion(1);
|
|
40631
|
+
return;
|
|
40632
|
+
}
|
|
40633
|
+
if (ck?.name === "escape") {
|
|
40634
|
+
this.cancelCompletion();
|
|
40635
|
+
return;
|
|
40636
|
+
}
|
|
40637
|
+
this.completionSession = null;
|
|
40638
|
+
}
|
|
40499
40639
|
if (this.onKeyInput && this.onKeyInput(str, key))
|
|
40500
40640
|
return;
|
|
40501
40641
|
const k = key;
|
|
@@ -40674,6 +40814,7 @@ class LineEditor {
|
|
|
40674
40814
|
}
|
|
40675
40815
|
}
|
|
40676
40816
|
submit() {
|
|
40817
|
+
this.completionSession = null;
|
|
40677
40818
|
if (this.questionCb) {
|
|
40678
40819
|
const cb = this.questionCb;
|
|
40679
40820
|
this.questionCb = null;
|
|
@@ -40729,6 +40870,7 @@ class LineEditor {
|
|
|
40729
40870
|
this.lines = [""];
|
|
40730
40871
|
this.row = 0;
|
|
40731
40872
|
this.col = 0;
|
|
40873
|
+
this.completionSession = null;
|
|
40732
40874
|
this.invalidateAnchor();
|
|
40733
40875
|
this.render();
|
|
40734
40876
|
if (this.onInterrupt) {
|
|
@@ -40753,6 +40895,7 @@ class LineEditor {
|
|
|
40753
40895
|
this.lines[this.row] = chars.join("");
|
|
40754
40896
|
}
|
|
40755
40897
|
insertText(text) {
|
|
40898
|
+
this.completionSession = null;
|
|
40756
40899
|
const sanitized = text.replace(/\r\n/g, `
|
|
40757
40900
|
`).replace(/\r/g, "");
|
|
40758
40901
|
const chars = this.currentChars();
|
|
@@ -40967,6 +41110,7 @@ class LineEditor {
|
|
|
40967
41110
|
}
|
|
40968
41111
|
}
|
|
40969
41112
|
applyHistoryEntry() {
|
|
41113
|
+
this.completionSession = null;
|
|
40970
41114
|
const entry = this.history[this.history.length - 1 - this.historyIndex];
|
|
40971
41115
|
this.lines = entry.split(`
|
|
40972
41116
|
`);
|
|
@@ -40976,6 +41120,49 @@ class LineEditor {
|
|
|
40976
41120
|
this.col = charLen(this.lines[this.row]);
|
|
40977
41121
|
this.render();
|
|
40978
41122
|
}
|
|
41123
|
+
replaceCompletionToken(from, removeText, insertText) {
|
|
41124
|
+
const line = this.lines[this.row];
|
|
41125
|
+
const head = line.slice(0, from);
|
|
41126
|
+
const tail = line.slice(from + removeText.length);
|
|
41127
|
+
this.lines[this.row] = head + insertText + tail;
|
|
41128
|
+
this.col = charLen(head) + charLen(insertText);
|
|
41129
|
+
}
|
|
41130
|
+
partialStart(line, partial) {
|
|
41131
|
+
const chars = Array.from(line);
|
|
41132
|
+
const beforeCursor = chars.slice(0, this.col).join("");
|
|
41133
|
+
let from = beforeCursor.lastIndexOf(partial);
|
|
41134
|
+
if (from === -1)
|
|
41135
|
+
from = line.lastIndexOf(partial);
|
|
41136
|
+
return from;
|
|
41137
|
+
}
|
|
41138
|
+
applyCompletionCandidate(index) {
|
|
41139
|
+
const s = this.completionSession;
|
|
41140
|
+
if (!s)
|
|
41141
|
+
return;
|
|
41142
|
+
const chosen = s.items[index].value;
|
|
41143
|
+
this.replaceCompletionToken(s.from, s.inserted, chosen);
|
|
41144
|
+
s.index = index;
|
|
41145
|
+
s.inserted = chosen;
|
|
41146
|
+
this.render();
|
|
41147
|
+
}
|
|
41148
|
+
cycleCompletion(delta) {
|
|
41149
|
+
const s = this.completionSession;
|
|
41150
|
+
if (!s)
|
|
41151
|
+
return;
|
|
41152
|
+
const n = s.items.length;
|
|
41153
|
+
if (n === 0)
|
|
41154
|
+
return;
|
|
41155
|
+
const next = (s.index + delta + n) % n;
|
|
41156
|
+
this.applyCompletionCandidate(next);
|
|
41157
|
+
}
|
|
41158
|
+
cancelCompletion() {
|
|
41159
|
+
const s = this.completionSession;
|
|
41160
|
+
if (!s)
|
|
41161
|
+
return;
|
|
41162
|
+
this.replaceCompletionToken(s.from, s.inserted, s.original);
|
|
41163
|
+
this.completionSession = null;
|
|
41164
|
+
this.render();
|
|
41165
|
+
}
|
|
40979
41166
|
tabComplete() {
|
|
40980
41167
|
if (!this.completer)
|
|
40981
41168
|
return;
|
|
@@ -40986,28 +41173,53 @@ class LineEditor {
|
|
|
40986
41173
|
return;
|
|
40987
41174
|
}
|
|
40988
41175
|
if (matches.length === 1) {
|
|
40989
|
-
const
|
|
40990
|
-
|
|
40991
|
-
let from = beforeCursor.lastIndexOf(partial);
|
|
40992
|
-
if (from === -1)
|
|
40993
|
-
from = line.lastIndexOf(partial);
|
|
40994
|
-
if (from === -1) {
|
|
41176
|
+
const from2 = this.partialStart(line, partial);
|
|
41177
|
+
if (from2 === -1) {
|
|
40995
41178
|
this.output.write("\x07");
|
|
40996
41179
|
return;
|
|
40997
41180
|
}
|
|
40998
|
-
|
|
40999
|
-
const tail = line.slice(from + partial.length);
|
|
41000
|
-
this.lines[this.row] = head + matches[0] + tail;
|
|
41001
|
-
this.col = charLen(head) + charLen(matches[0]);
|
|
41181
|
+
this.replaceCompletionToken(from2, partial, matches[0].value);
|
|
41002
41182
|
this.render();
|
|
41003
41183
|
return;
|
|
41004
41184
|
}
|
|
41005
|
-
const
|
|
41185
|
+
const from = this.partialStart(line, partial);
|
|
41186
|
+
if (from === -1) {
|
|
41187
|
+
this.output.write("\x07");
|
|
41188
|
+
return;
|
|
41189
|
+
}
|
|
41190
|
+
const width = this.columns();
|
|
41191
|
+
const limit = width - 1;
|
|
41192
|
+
const rows = matches.map((m) => {
|
|
41193
|
+
const plain = m.description ? ` ${m.value} ${m.description}` : ` ${m.value}`;
|
|
41194
|
+
if (limit <= 0)
|
|
41195
|
+
return "";
|
|
41196
|
+
if (stringWidth(plain) <= limit) {
|
|
41197
|
+
return m.description ? ` ${m.value} ${pc.dim(m.description)}` : ` ${m.value}`;
|
|
41198
|
+
}
|
|
41199
|
+
let acc = "";
|
|
41200
|
+
let w = 0;
|
|
41201
|
+
for (const ch of plain) {
|
|
41202
|
+
const cw = stringWidth(ch);
|
|
41203
|
+
if (w + cw > limit - 1)
|
|
41204
|
+
break;
|
|
41205
|
+
acc += ch;
|
|
41206
|
+
w += cw;
|
|
41207
|
+
}
|
|
41208
|
+
return acc + "…";
|
|
41209
|
+
});
|
|
41006
41210
|
this.output.write(`\r
|
|
41007
41211
|
` + rows.join(`\r
|
|
41008
41212
|
`) + `\r
|
|
41009
41213
|
`);
|
|
41010
41214
|
this.prevCursorRow += rows.length + 1;
|
|
41215
|
+
this.completionSession = {
|
|
41216
|
+
items: matches,
|
|
41217
|
+
index: 0,
|
|
41218
|
+
from,
|
|
41219
|
+
original: partial,
|
|
41220
|
+
inserted: matches[0].value
|
|
41221
|
+
};
|
|
41222
|
+
this.replaceCompletionToken(from, partial, matches[0].value);
|
|
41011
41223
|
this.render();
|
|
41012
41224
|
}
|
|
41013
41225
|
clearScreen() {
|
|
@@ -41144,10 +41356,19 @@ class LineEditor {
|
|
|
41144
41356
|
|
|
41145
41357
|
// src/cli/repl.ts
|
|
41146
41358
|
import { existsSync as existsSync61, mkdirSync as mkdirSync25, readFileSync as readFileSync42, writeFileSync as writeFileSync24 } from "fs";
|
|
41147
|
-
import { dirname as dirname26, join as
|
|
41359
|
+
import { dirname as dirname26, join as join56 } from "path";
|
|
41148
41360
|
import { homedir as homedir18 } from "os";
|
|
41149
41361
|
|
|
41150
41362
|
// src/cli/completer.ts
|
|
41363
|
+
import { readdirSync as readdirSync19, statSync as statSync11 } from "fs";
|
|
41364
|
+
import { join as join54 } from "path";
|
|
41365
|
+
function buildSlashCompletions(commands) {
|
|
41366
|
+
return Array.from(commands.entries()).map(([name, cmd]) => ({
|
|
41367
|
+
name,
|
|
41368
|
+
description: cmd.description
|
|
41369
|
+
}));
|
|
41370
|
+
}
|
|
41371
|
+
|
|
41151
41372
|
class SlashCommandProvider {
|
|
41152
41373
|
name = "slash-commands";
|
|
41153
41374
|
commands;
|
|
@@ -41158,7 +41379,7 @@ class SlashCommandProvider {
|
|
|
41158
41379
|
return ctx.tokenIndex === 0 && ctx.line.startsWith("/");
|
|
41159
41380
|
}
|
|
41160
41381
|
complete(ctx) {
|
|
41161
|
-
return this.commands.filter((c) => c.startsWith(ctx.partial.slice(1))).map((c) => `/${c}
|
|
41382
|
+
return this.commands.filter((c) => c.name.startsWith(ctx.partial.slice(1))).map((c) => ({ value: `/${c.name}`, description: c.description }));
|
|
41162
41383
|
}
|
|
41163
41384
|
}
|
|
41164
41385
|
|
|
@@ -41168,13 +41389,13 @@ class SubcommandProvider {
|
|
|
41168
41389
|
subcommands;
|
|
41169
41390
|
constructor(command, subcommands) {
|
|
41170
41391
|
this.command = command;
|
|
41171
|
-
this.subcommands = subcommands;
|
|
41392
|
+
this.subcommands = subcommands.map((s) => typeof s === "string" ? { name: s } : s);
|
|
41172
41393
|
}
|
|
41173
41394
|
match(ctx) {
|
|
41174
41395
|
return ctx.tokenIndex === 1 && ctx.tokens[0] === `/${this.command}`;
|
|
41175
41396
|
}
|
|
41176
41397
|
complete(ctx) {
|
|
41177
|
-
return this.subcommands.filter((s) => s.startsWith(ctx.partial));
|
|
41398
|
+
return this.subcommands.filter((s) => s.name.startsWith(ctx.partial)).map((s) => ({ value: s.name, description: s.description }));
|
|
41178
41399
|
}
|
|
41179
41400
|
}
|
|
41180
41401
|
|
|
@@ -41188,9 +41409,44 @@ class ModelArgProvider {
|
|
|
41188
41409
|
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
|
|
41189
41410
|
}
|
|
41190
41411
|
complete(ctx) {
|
|
41191
|
-
return this.getModels().filter((m) => m.startsWith(ctx.partial));
|
|
41412
|
+
return this.getModels().filter((m) => m.startsWith(ctx.partial)).map((m) => ({ value: m }));
|
|
41413
|
+
}
|
|
41414
|
+
}
|
|
41415
|
+
|
|
41416
|
+
class FilePathProvider {
|
|
41417
|
+
name = "file-paths";
|
|
41418
|
+
root;
|
|
41419
|
+
constructor(root) {
|
|
41420
|
+
this.root = root;
|
|
41421
|
+
}
|
|
41422
|
+
match(ctx) {
|
|
41423
|
+
return ctx.tokenIndex >= 1 && (ctx.partial.includes("/") || ctx.partial.includes("\\"));
|
|
41424
|
+
}
|
|
41425
|
+
complete(ctx) {
|
|
41426
|
+
try {
|
|
41427
|
+
const cwd = this.root ?? process.cwd();
|
|
41428
|
+
const partialPath = ctx.partial;
|
|
41429
|
+
const lastSlash = Math.max(partialPath.lastIndexOf("/"), partialPath.lastIndexOf("\\"));
|
|
41430
|
+
const dirPath = lastSlash > 0 ? partialPath.slice(0, lastSlash) : ".";
|
|
41431
|
+
const prefix = lastSlash > 0 ? partialPath.slice(0, lastSlash + 1) : "";
|
|
41432
|
+
const fullPath = join54(cwd, dirPath);
|
|
41433
|
+
const entries = readdirSync19(fullPath);
|
|
41434
|
+
const matches = [];
|
|
41435
|
+
for (const entry of entries) {
|
|
41436
|
+
const entryPath = join54(fullPath, entry);
|
|
41437
|
+
const stat2 = statSync11(entryPath);
|
|
41438
|
+
const completion = prefix + entry + (stat2.isDirectory() ? "/" : "");
|
|
41439
|
+
if (completion.startsWith(ctx.partial)) {
|
|
41440
|
+
matches.push(completion);
|
|
41441
|
+
}
|
|
41442
|
+
}
|
|
41443
|
+
return matches.slice(0, 20).map((m) => ({ value: m }));
|
|
41444
|
+
} catch {
|
|
41445
|
+
return [];
|
|
41446
|
+
}
|
|
41192
41447
|
}
|
|
41193
41448
|
}
|
|
41449
|
+
|
|
41194
41450
|
class ProviderArgProvider {
|
|
41195
41451
|
name = "provider-args";
|
|
41196
41452
|
providers;
|
|
@@ -41201,7 +41457,7 @@ class ProviderArgProvider {
|
|
|
41201
41457
|
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/provider" && ctx.tokens[1] === "use";
|
|
41202
41458
|
}
|
|
41203
41459
|
complete(ctx) {
|
|
41204
|
-
return this.providers.filter((p) => p.startsWith(ctx.partial));
|
|
41460
|
+
return this.providers.filter((p) => p.startsWith(ctx.partial)).map((p) => ({ value: p }));
|
|
41205
41461
|
}
|
|
41206
41462
|
}
|
|
41207
41463
|
|
|
@@ -41216,9 +41472,8 @@ class SessionNameProvider {
|
|
|
41216
41472
|
}
|
|
41217
41473
|
complete(ctx) {
|
|
41218
41474
|
const names = this.getNames();
|
|
41219
|
-
|
|
41220
|
-
|
|
41221
|
-
return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
|
|
41475
|
+
const filtered = !ctx.partial ? names : names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
|
|
41476
|
+
return filtered.map((n) => ({ value: n }));
|
|
41222
41477
|
}
|
|
41223
41478
|
}
|
|
41224
41479
|
|
|
@@ -41240,9 +41495,8 @@ class SkillNameProvider {
|
|
|
41240
41495
|
if (!source)
|
|
41241
41496
|
return [];
|
|
41242
41497
|
const names = source();
|
|
41243
|
-
|
|
41244
|
-
|
|
41245
|
-
return names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
|
|
41498
|
+
const filtered = !ctx.partial ? names : names.filter((n) => n.toLowerCase().includes(ctx.partial.toLowerCase()));
|
|
41499
|
+
return filtered.map((n) => ({ value: n }));
|
|
41246
41500
|
}
|
|
41247
41501
|
}
|
|
41248
41502
|
|
|
@@ -41354,19 +41608,22 @@ function toolCategory(tool) {
|
|
|
41354
41608
|
}
|
|
41355
41609
|
return "meta";
|
|
41356
41610
|
}
|
|
41357
|
-
function
|
|
41611
|
+
function toolAccent(tool) {
|
|
41358
41612
|
switch (toolCategory(tool)) {
|
|
41359
41613
|
case "recon":
|
|
41360
|
-
return pc.cyan
|
|
41614
|
+
return pc.cyan;
|
|
41361
41615
|
case "mutate":
|
|
41362
|
-
return pc.magenta
|
|
41616
|
+
return pc.magenta;
|
|
41363
41617
|
case "execute":
|
|
41364
|
-
return pc.blue
|
|
41618
|
+
return pc.blue;
|
|
41365
41619
|
case "meta":
|
|
41366
41620
|
default:
|
|
41367
|
-
return pc.dim
|
|
41621
|
+
return pc.dim;
|
|
41368
41622
|
}
|
|
41369
41623
|
}
|
|
41624
|
+
function actionMarkFor(tool) {
|
|
41625
|
+
return toolAccent(tool)(glyph.action);
|
|
41626
|
+
}
|
|
41370
41627
|
function successMark() {
|
|
41371
41628
|
return pc.green(glyph.success);
|
|
41372
41629
|
}
|
|
@@ -41861,11 +42118,7 @@ class Renderer {
|
|
|
41861
42118
|
meta(chunk) {
|
|
41862
42119
|
this.spinner.stop();
|
|
41863
42120
|
if (this.thoughtStarted) {
|
|
41864
|
-
|
|
41865
|
-
this.channel.writeRaw(`
|
|
41866
|
-
${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
|
|
41867
|
-
this.thoughtHeaderPrinted = true;
|
|
41868
|
-
}
|
|
42121
|
+
this.writeThoughtHeader();
|
|
41869
42122
|
this.channel.writeRaw(pc.dim(chunk));
|
|
41870
42123
|
} else if (this.card) {
|
|
41871
42124
|
this.writeInlineBody(chunk);
|
|
@@ -41901,13 +42154,16 @@ ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
|
|
|
41901
42154
|
reasoning(chunk) {
|
|
41902
42155
|
this.spinner.stop();
|
|
41903
42156
|
this.flushRecon();
|
|
41904
|
-
|
|
41905
|
-
this.channel.writeRaw(`
|
|
41906
|
-
${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
|
|
41907
|
-
this.thoughtHeaderPrinted = true;
|
|
41908
|
-
}
|
|
42157
|
+
this.writeThoughtHeader();
|
|
41909
42158
|
this.channel.writeRaw(pc.dim(chunk));
|
|
41910
42159
|
}
|
|
42160
|
+
writeThoughtHeader() {
|
|
42161
|
+
if (!this.thoughtStarted || this.thoughtHeaderPrinted)
|
|
42162
|
+
return;
|
|
42163
|
+
this.channel.writeRaw(`
|
|
42164
|
+
${pc.magenta(glyph.thinking)} ${pc.magenta(this.thinkingLabel)} ${pc.dim("·")} `);
|
|
42165
|
+
this.thoughtHeaderPrinted = true;
|
|
42166
|
+
}
|
|
41911
42167
|
thinkingStart() {
|
|
41912
42168
|
this.flushRecon();
|
|
41913
42169
|
this.thoughtStarted = true;
|
|
@@ -41946,6 +42202,7 @@ ${pc.magenta(glyph.thinking)} ${pc.dim(this.thinkingLabel)} `);
|
|
|
41946
42202
|
const summary = humanArgs(tool, displayArgs);
|
|
41947
42203
|
const label = friendlyTool(tool);
|
|
41948
42204
|
const mark = actionMarkFor(tool);
|
|
42205
|
+
const accent = toolAccent(tool);
|
|
41949
42206
|
const deferred = this.verbosity === "quiet";
|
|
41950
42207
|
this.card = {
|
|
41951
42208
|
tool,
|
|
@@ -41964,7 +42221,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
41964
42221
|
`);
|
|
41965
42222
|
}
|
|
41966
42223
|
if (!deferred) {
|
|
41967
|
-
this.channel.writeRaw(`${mark} ${label}${summary ? ` ${pc.dim(summary)}` : ""}
|
|
42224
|
+
this.channel.writeRaw(`${mark} ${accent(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}
|
|
41968
42225
|
`);
|
|
41969
42226
|
}
|
|
41970
42227
|
if (!SPINNERLESS_TOOLS.has(tool)) {
|
|
@@ -41981,7 +42238,8 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
41981
42238
|
this.reconBuffer = [];
|
|
41982
42239
|
if (items.length === 1) {
|
|
41983
42240
|
const it = items[0];
|
|
41984
|
-
const
|
|
42241
|
+
const accent = toolAccent(it.tool);
|
|
42242
|
+
const head = `${it.mark} ${accent(it.label)}${it.summary ? ` ${pc.dim("·")} ${pc.dim(it.summary)}` : ""}`;
|
|
41985
42243
|
this.channel.writeRaw(`${head} ${successMark()} ${pc.dim(formatDuration(it.duration))}
|
|
41986
42244
|
`);
|
|
41987
42245
|
return;
|
|
@@ -42018,13 +42276,19 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
42018
42276
|
}
|
|
42019
42277
|
if (deferred) {
|
|
42020
42278
|
if (!error && RECON_TOOLS.has(this.card.tool)) {
|
|
42021
|
-
this.reconBuffer.push({
|
|
42279
|
+
this.reconBuffer.push({
|
|
42280
|
+
tool: this.card.tool,
|
|
42281
|
+
mark: leadMark,
|
|
42282
|
+
label,
|
|
42283
|
+
summary,
|
|
42284
|
+
duration
|
|
42285
|
+
});
|
|
42022
42286
|
this.recordLast(label, summary, error, duration);
|
|
42023
42287
|
this.card = null;
|
|
42024
42288
|
return;
|
|
42025
42289
|
}
|
|
42026
42290
|
this.flushRecon();
|
|
42027
|
-
const head = `${leadMark} ${label}${summary ? ` ${pc.dim(summary)}` : ""}`;
|
|
42291
|
+
const head = `${leadMark} ${toolAccent(this.card.tool)(label)}${summary ? ` ${pc.dim("·")} ${pc.dim(summary)}` : ""}`;
|
|
42028
42292
|
this.channel.writeRaw(`${head} ${tail}
|
|
42029
42293
|
`);
|
|
42030
42294
|
if (error && firstBody) {
|
|
@@ -42049,6 +42313,7 @@ ${pc.dim(glyph.detail)} ${pc.cyan(stepContext)}
|
|
|
42049
42313
|
const firstBody = this.card?.firstBody ?? "";
|
|
42050
42314
|
const status = !error ? "ok" : firstBody.startsWith("⚠") ? "warn" : "error";
|
|
42051
42315
|
recordLastAction({
|
|
42316
|
+
tool: this.card?.tool ?? "",
|
|
42052
42317
|
label,
|
|
42053
42318
|
args: summary,
|
|
42054
42319
|
status,
|
|
@@ -42425,10 +42690,10 @@ class Repl {
|
|
|
42425
42690
|
this.execModule = execModule;
|
|
42426
42691
|
this.slog = new SessionLogger(sessionManager, logger4);
|
|
42427
42692
|
this.bindSessionOutput();
|
|
42428
|
-
this.configDir = configDir ||
|
|
42693
|
+
this.configDir = configDir || join56(homedir18(), ".mma");
|
|
42429
42694
|
this.baseDir = baseDir || process.cwd();
|
|
42430
42695
|
this.noAgentsMd = noAgentsMd === true;
|
|
42431
|
-
this.historyPath = historyPath ??
|
|
42696
|
+
this.historyPath = historyPath ?? join56(homedir18(), ".mma", "repl-history");
|
|
42432
42697
|
this.loadHistory();
|
|
42433
42698
|
const editorCompleter = (line) => {
|
|
42434
42699
|
const [matches, partial] = this.completer.complete(line);
|
|
@@ -42436,6 +42701,10 @@ class Repl {
|
|
|
42436
42701
|
return [matches, partial];
|
|
42437
42702
|
return [[], line];
|
|
42438
42703
|
};
|
|
42704
|
+
const readlineCompleter = (line) => {
|
|
42705
|
+
const [items, partial] = editorCompleter(line);
|
|
42706
|
+
return [items.map((i) => i.value), partial];
|
|
42707
|
+
};
|
|
42439
42708
|
if (process.stdin.isTTY) {
|
|
42440
42709
|
this.rl = new LineEditor({
|
|
42441
42710
|
input: process.stdin,
|
|
@@ -42453,7 +42722,7 @@ class Repl {
|
|
|
42453
42722
|
history: this.history,
|
|
42454
42723
|
historySize: this.maxHistory,
|
|
42455
42724
|
tabSize: 2,
|
|
42456
|
-
completer:
|
|
42725
|
+
completer: readlineCompleter
|
|
42457
42726
|
});
|
|
42458
42727
|
}
|
|
42459
42728
|
this.output = output ?? getDefaultChannel();
|
|
@@ -42508,7 +42777,7 @@ class Repl {
|
|
|
42508
42777
|
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config2(), exports_config));
|
|
42509
42778
|
const { bootstrap: bootstrap2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
|
|
42510
42779
|
this.agent.shutdown();
|
|
42511
|
-
const projectConfigPath =
|
|
42780
|
+
const projectConfigPath = join56(this.baseDir, ".mmrc");
|
|
42512
42781
|
const { config: freshConfig } = loadConfig2({
|
|
42513
42782
|
configDir: this.configDir,
|
|
42514
42783
|
projectConfigPath
|
|
@@ -42548,7 +42817,7 @@ class Repl {
|
|
|
42548
42817
|
}
|
|
42549
42818
|
setupCompleter() {
|
|
42550
42819
|
this.completer.reset();
|
|
42551
|
-
const slashCommands =
|
|
42820
|
+
const slashCommands = buildSlashCompletions(this.commands);
|
|
42552
42821
|
this.completer.registerProvider(new SlashCommandProvider(slashCommands));
|
|
42553
42822
|
if (this.sessionManager) {
|
|
42554
42823
|
this.completer.registerProvider(new SessionNameProvider(() => this.sessionManager.list().map((s) => s.name)));
|
|
@@ -42564,6 +42833,7 @@ class Repl {
|
|
|
42564
42833
|
this.completer.registerProvider(new ProviderArgProvider(entryLabels));
|
|
42565
42834
|
}
|
|
42566
42835
|
this.completer.registerProvider(new ModelArgProvider(() => this.modelCache));
|
|
42836
|
+
this.completer.registerProvider(new FilePathProvider(this.baseDir));
|
|
42567
42837
|
this.refreshModelCache();
|
|
42568
42838
|
}
|
|
42569
42839
|
refreshModelCache() {
|
|
@@ -42744,7 +43014,8 @@ ${t("repl.interrupt")}`));
|
|
|
42744
43014
|
return;
|
|
42745
43015
|
}
|
|
42746
43016
|
const mark = last2.status === "error" ? pc.red(glyph.failure) : last2.status === "warn" ? pc.yellow(glyph.warning) : pc.green(glyph.success);
|
|
42747
|
-
const
|
|
43017
|
+
const accent = toolAccent(last2.tool);
|
|
43018
|
+
const head = `${accent(glyph.action)} ${accent(last2.label)}${last2.args ? ` ${pc.dim("·")} ${pc.dim(last2.args)}` : ""} ${mark} ${pc.dim(`${last2.durationMs}ms`)}`;
|
|
42748
43019
|
const lines = [head, ...last2.body.map((l) => ` ${l}`)];
|
|
42749
43020
|
this.output.writeLine(`
|
|
42750
43021
|
${lines.join(`
|
|
@@ -43022,9 +43293,9 @@ ${divider()}`);
|
|
|
43022
43293
|
row(t("repl.agents_label"), pc.red(t("repl.disabled")));
|
|
43023
43294
|
} else {
|
|
43024
43295
|
const agentsMdCandidates = [
|
|
43025
|
-
|
|
43026
|
-
|
|
43027
|
-
|
|
43296
|
+
join56(this.baseDir, "AGENTS.md"),
|
|
43297
|
+
join56(this.baseDir, ".mma", "AGENTS.md"),
|
|
43298
|
+
join56(this.configDir, "AGENTS.md")
|
|
43028
43299
|
];
|
|
43029
43300
|
const foundAgents = agentsMdCandidates.filter((p) => existsSync61(p));
|
|
43030
43301
|
if (foundAgents.length > 0) {
|
|
@@ -43037,7 +43308,7 @@ ${divider()}`);
|
|
|
43037
43308
|
}
|
|
43038
43309
|
const meta = this.sessionManager?.getActiveMeta();
|
|
43039
43310
|
if (meta) {
|
|
43040
|
-
const sessionPath =
|
|
43311
|
+
const sessionPath = join56(this.configDir, "sessions", meta.id);
|
|
43041
43312
|
row(t("repl.session_label"), `${pc.cyan(meta.name)} ${pc.dim(`(${meta.id.slice(0, 12)})`)} — ${meta.messageCount} msgs ${pc.dim(sessionPath)}`);
|
|
43042
43313
|
}
|
|
43043
43314
|
const isTty2 = process.stdout.isTTY === true;
|
|
@@ -43049,9 +43320,9 @@ ${divider()}`);
|
|
|
43049
43320
|
try {
|
|
43050
43321
|
const { existsSync: exists } = await import("fs");
|
|
43051
43322
|
const { join: pathJoin } = await import("path");
|
|
43052
|
-
const { hasDomainFiles:
|
|
43323
|
+
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
43053
43324
|
const legacyPath = pathJoin(this.configDir, "config.json");
|
|
43054
|
-
if (exists(legacyPath) && !
|
|
43325
|
+
if (exists(legacyPath) && !hasDomainFiles2(this.configDir)) {
|
|
43055
43326
|
this.output.writeLine(pc.yellow(` ${t("config.legacy_hint")}`));
|
|
43056
43327
|
this.output.writeLine("");
|
|
43057
43328
|
}
|
|
@@ -43166,51 +43437,7 @@ function printRunResult(result, flush, channel = getDefaultChannel()) {
|
|
|
43166
43437
|
|
|
43167
43438
|
// src/cli/main.ts
|
|
43168
43439
|
init_setup();
|
|
43169
|
-
|
|
43170
|
-
// src/cli/setup-order.ts
|
|
43171
|
-
var SECURITY_BASH_BLOCKLIST = [
|
|
43172
|
-
"rm",
|
|
43173
|
-
"dd",
|
|
43174
|
-
"chmod",
|
|
43175
|
-
"wget",
|
|
43176
|
-
"curl",
|
|
43177
|
-
"scp",
|
|
43178
|
-
"ssh",
|
|
43179
|
-
"nc",
|
|
43180
|
-
"netcat",
|
|
43181
|
-
"sudo",
|
|
43182
|
-
"su",
|
|
43183
|
-
"kill",
|
|
43184
|
-
"pkill",
|
|
43185
|
-
"killall",
|
|
43186
|
-
"shutdown",
|
|
43187
|
-
"reboot"
|
|
43188
|
-
];
|
|
43189
|
-
function applyAnswersToConfig(config, a) {
|
|
43190
|
-
config.provider = {
|
|
43191
|
-
...config.provider,
|
|
43192
|
-
type: a.provider,
|
|
43193
|
-
baseUrl: a.apiBase,
|
|
43194
|
-
apiKey: a.apiKey
|
|
43195
|
-
};
|
|
43196
|
-
config.model = a.model;
|
|
43197
|
-
config.contextWindow = a.contextWindow;
|
|
43198
|
-
config.maxToolIterations = a.maxToolIterations;
|
|
43199
|
-
config.locale = a.locale;
|
|
43200
|
-
if (config.security) {
|
|
43201
|
-
config.security.enabled = true;
|
|
43202
|
-
config.security.bash.enabled = true;
|
|
43203
|
-
config.security.bash.blockDangerousFlags = a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
43204
|
-
if (a.securityBashBlock) {
|
|
43205
|
-
config.security.bash.blacklist = [
|
|
43206
|
-
...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST])
|
|
43207
|
-
];
|
|
43208
|
-
}
|
|
43209
|
-
if (!a.securityPathsDeny)
|
|
43210
|
-
config.security.paths.denied = [];
|
|
43211
|
-
}
|
|
43212
|
-
return config;
|
|
43213
|
-
}
|
|
43440
|
+
init_setup_order();
|
|
43214
43441
|
|
|
43215
43442
|
// src/cli/relaunch.ts
|
|
43216
43443
|
init_output();
|
|
@@ -43270,7 +43497,7 @@ init_config2();
|
|
|
43270
43497
|
init_i18n();
|
|
43271
43498
|
init_colors();
|
|
43272
43499
|
import { existsSync as existsSync62 } from "fs";
|
|
43273
|
-
import { join as
|
|
43500
|
+
import { join as join58, dirname as dirname27 } from "path";
|
|
43274
43501
|
import { homedir as homedir20 } from "os";
|
|
43275
43502
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
43276
43503
|
|
|
@@ -43392,9 +43619,9 @@ init_data_sanitizer();
|
|
|
43392
43619
|
init_i18n();
|
|
43393
43620
|
init_utils();
|
|
43394
43621
|
import { appendFileSync as appendFileSync8, mkdirSync as mkdirSync26 } from "fs";
|
|
43395
|
-
import { join as
|
|
43622
|
+
import { join as join57 } from "path";
|
|
43396
43623
|
import { homedir as homedir19 } from "os";
|
|
43397
|
-
var CRASH_LOG_DIR =
|
|
43624
|
+
var CRASH_LOG_DIR = join57(homedir19(), ".mma", "logs");
|
|
43398
43625
|
var CRASH_LOG_FILE = "crash.jsonl";
|
|
43399
43626
|
function formatCrashEntry(type2, err) {
|
|
43400
43627
|
const message = errMsg(err);
|
|
@@ -43410,7 +43637,7 @@ function formatCrashEntry(type2, err) {
|
|
|
43410
43637
|
function writeCrashEntry(dir, entry) {
|
|
43411
43638
|
try {
|
|
43412
43639
|
mkdirSync26(dir, { recursive: true });
|
|
43413
|
-
appendFileSync8(
|
|
43640
|
+
appendFileSync8(join57(dir, CRASH_LOG_FILE), JSON.stringify(entry) + `
|
|
43414
43641
|
`, "utf-8");
|
|
43415
43642
|
} catch {}
|
|
43416
43643
|
}
|
|
@@ -43538,6 +43765,9 @@ async function main() {
|
|
|
43538
43765
|
}
|
|
43539
43766
|
const jsonSink = jsonOpt ? new JsonOutputSink(defaultOutputBus) : undefined;
|
|
43540
43767
|
const prompt = program2.args.join(" ");
|
|
43768
|
+
const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
|
|
43769
|
+
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
43770
|
+
const virgin = !existsSync62(join58(mmaDir, "config.json")) && !hasDomainFiles2(mmaDir);
|
|
43541
43771
|
const { agent, config, baseDir, legacyDetected, contextProbe } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete, reasoningLevel, contextLog);
|
|
43542
43772
|
if (verboseOpt)
|
|
43543
43773
|
config.ui.verbosity = "verbose";
|
|
@@ -43545,6 +43775,9 @@ async function main() {
|
|
|
43545
43775
|
if (legacyDetected) {
|
|
43546
43776
|
getDefaultChannel().writeLine(pc.yellow(` ${t("config.legacy_hint")}`), "stderr");
|
|
43547
43777
|
}
|
|
43778
|
+
if (virgin && !jsonOpt) {
|
|
43779
|
+
getDefaultChannel().writeLine(pc.yellow(` ${t("cli.first_run_oneshot")}`), "stderr");
|
|
43780
|
+
}
|
|
43548
43781
|
if (contextProbe && !jsonOpt) {
|
|
43549
43782
|
try {
|
|
43550
43783
|
const probe = await contextProbe;
|
|
@@ -43620,16 +43853,16 @@ async function main() {
|
|
|
43620
43853
|
await updater?.waitForIdle();
|
|
43621
43854
|
process.exit(exitCode);
|
|
43622
43855
|
} else {
|
|
43623
|
-
const mmaDir = process.env.MMA_CONFIG_DIR ||
|
|
43624
|
-
const legacyConfigPath =
|
|
43856
|
+
const mmaDir = process.env.MMA_CONFIG_DIR || join58(homedir20(), ".mma");
|
|
43857
|
+
const legacyConfigPath = join58(mmaDir, "config.json");
|
|
43625
43858
|
let hasAnyConfig = existsSync62(legacyConfigPath);
|
|
43626
43859
|
if (!hasAnyConfig) {
|
|
43627
43860
|
try {
|
|
43628
|
-
const { hasDomainFiles:
|
|
43629
|
-
hasAnyConfig =
|
|
43861
|
+
const { hasDomainFiles: hasDomainFiles2 } = await Promise.resolve().then(() => (init_domains(), exports_domains));
|
|
43862
|
+
hasAnyConfig = hasDomainFiles2(mmaDir);
|
|
43630
43863
|
} catch {}
|
|
43631
43864
|
}
|
|
43632
|
-
const projectConfigPath = projectDir ?
|
|
43865
|
+
const projectConfigPath = projectDir ? join58(projectDir, ".mmrc") : join58(process.cwd(), ".mmrc");
|
|
43633
43866
|
if (!hasAnyConfig && !exitOnComplete && process.env.MMA_POST_SETUP !== "1") {
|
|
43634
43867
|
getDefaultChannel().writeLine(`
|
|
43635
43868
|
` + t("cli.first_run") + `
|