micro-models-agent 0.51.1 → 0.52.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/dist/cli/commands.js +162 -38
- package/dist/cli/completer.js +5 -5
- package/dist/cli/main.js +42 -54
- package/dist/cli/repl-commands.js +138 -38
- package/dist/cli/repl.js +175 -89
- package/dist/cli/run-result.js +11 -0
- package/dist/cli/security-commands.js +6 -6
- package/dist/cli/setup.js +21 -15
- package/dist/config/config.js +54 -27
- package/dist/config/defaults.js +17 -0
- package/dist/config/domains.js +179 -0
- package/dist/config/index.js +2 -1
- package/dist/config/security.js +28 -8
- package/dist/core/agent.js +162 -30
- package/dist/core/bootstrap.js +94 -17
- package/dist/core/crash-handler.js +51 -0
- package/dist/core/environment.js +199 -0
- package/dist/core/session-logger.js +60 -6
- package/dist/core/version.js +2 -0
- package/dist/i18n/en.json +120 -39
- package/dist/i18n/ru.json +91 -10
- package/dist/llm/openai-compat.js +191 -53
- package/dist/llm/orchestrator.js +5 -3
- package/dist/logger/app-logger.js +50 -4
- package/dist/main.js +1288 -635
- package/dist/modules/browser/session.js +4 -0
- package/dist/modules/certification/cli.js +58 -19
- package/dist/modules/certification/loader.js +2 -1
- package/dist/modules/certification/manifest.js +22 -14
- package/dist/modules/certification/runner.js +91 -5
- package/dist/modules/certification/scenarios.js +290 -7
- package/dist/modules/context/fact-extractor.js +6 -0
- package/dist/modules/context/manager.js +19 -2
- package/dist/modules/execution/audit-runners.js +61 -7
- package/dist/modules/execution/execution-plugin.js +219 -60
- package/dist/modules/execution/module.js +207 -18
- package/dist/modules/execution/moe-executor.js +33 -20
- package/dist/modules/execution/plan-store.js +39 -0
- package/dist/modules/execution/plan-tool.js +188 -19
- package/dist/modules/execution/planner.js +27 -23
- package/dist/modules/execution/stuck-detector.js +244 -8
- package/dist/modules/execution/tracker.js +8 -6
- package/dist/modules/execution/verifier.js +15 -2
- package/dist/modules/hallucination/detector.js +4 -0
- package/dist/modules/hallucination/factual.js +45 -5
- package/dist/modules/indexer/module.js +1 -0
- package/dist/modules/lsp/client.js +123 -12
- package/dist/modules/lsp/index.js +1 -1
- package/dist/modules/lsp/module.js +30 -2
- package/dist/modules/lsp/probe.js +11 -1
- package/dist/modules/lsp/startup-check.js +5 -2
- package/dist/modules/plugins/builtin/lint-on-write.js +144 -41
- package/dist/modules/plugins/manager.js +57 -13
- package/dist/modules/pricing/index.js +61 -0
- package/dist/modules/pricing/prices.js +129 -0
- package/dist/modules/providers/create.js +22 -0
- package/dist/modules/providers/fallback.js +79 -0
- package/dist/modules/providers/health.js +46 -0
- package/dist/modules/providers/index.js +5 -0
- package/dist/modules/providers/manager.js +161 -0
- package/dist/modules/providers/presets.js +128 -0
- package/dist/modules/providers/registry.js +22 -0
- package/dist/modules/providers/types.js +1 -0
- package/dist/modules/registry.js +1 -0
- package/dist/modules/security/command-validator.js +14 -0
- package/dist/modules/security/encryption.js +6 -6
- package/dist/modules/security/network-validator.js +17 -0
- package/dist/modules/security/path-validator.js +22 -26
- package/dist/modules/session/store.js +10 -10
- package/dist/tools/approve.js +1 -0
- package/dist/tools/attach-image.js +12 -0
- package/dist/tools/bash.js +27 -4
- package/dist/tools/browser.js +1 -0
- package/dist/tools/chunk-query.js +1 -0
- package/dist/tools/create-dir.js +1 -0
- package/dist/tools/delete-file.js +1 -0
- package/dist/tools/download-file.js +1 -0
- package/dist/tools/edit-file.js +2 -1
- package/dist/tools/enable-tools.js +1 -0
- package/dist/tools/executor.js +17 -7
- package/dist/tools/file-info.js +1 -0
- package/dist/tools/glob-tool.js +1 -0
- package/dist/tools/grep-tool.js +54 -13
- package/dist/tools/list-dir.js +1 -0
- package/dist/tools/load-skill.js +1 -0
- package/dist/tools/mcp-call.js +1 -0
- package/dist/tools/move-file.js +1 -0
- package/dist/tools/path-utils.js +51 -1
- package/dist/tools/pipeline-run.js +1 -0
- package/dist/tools/process-kill.js +11 -0
- package/dist/tools/process-list.js +1 -0
- package/dist/tools/process-log.js +9 -0
- package/dist/tools/question.js +1 -0
- package/dist/tools/read-file.js +94 -6
- package/dist/tools/recall.js +1 -0
- package/dist/tools/remember.js +1 -0
- package/dist/tools/scope-check.js +7 -5
- package/dist/tools/search-history.js +1 -0
- package/dist/tools/subagent.js +4 -4
- package/dist/tools/web-browse.js +1 -0
- package/dist/tools/web-fetch.js +27 -6
- package/dist/tools/web-search.js +70 -43
- package/dist/tools/write-file.js +1 -0
- package/dist/ui/line-editor.js +142 -23
- package/dist/ui/line-math.js +8 -4
- package/dist/ui/renderer.js +57 -7
- package/package.json +50 -48
package/dist/cli/commands.js
CHANGED
|
@@ -3,30 +3,16 @@ import { bootstrap } from "../core/bootstrap";
|
|
|
3
3
|
import { saveConfig } from "../config/config";
|
|
4
4
|
import { runSetup } from "./setup";
|
|
5
5
|
import { t } from "../i18n/index";
|
|
6
|
+
import { pc } from "../ui/colors";
|
|
6
7
|
import { join, dirname } from "path";
|
|
7
8
|
import { homedir } from "os";
|
|
8
|
-
import { existsSync
|
|
9
|
+
import { existsSync } from "fs";
|
|
9
10
|
import { createSecurityCommand } from "./security-commands";
|
|
10
11
|
import { createPluginCommand } from "./plugin-commands";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
for (const p of candidates) {
|
|
16
|
-
if (existsSync(p)) {
|
|
17
|
-
try {
|
|
18
|
-
const raw = JSON.parse(readFileSync(p, "utf8"));
|
|
19
|
-
if (raw.version)
|
|
20
|
-
return raw.version;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// Broken package.json — fall through to the next candidate
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return "0.0.0";
|
|
28
|
-
}
|
|
29
|
-
const version = readVersion();
|
|
12
|
+
import { HOSTED_BASE_URLS } from "../modules/providers/presets";
|
|
13
|
+
import { probeProviders, targetsFromProviders } from "../modules/providers/health";
|
|
14
|
+
import { readMmaVersion } from "../core/version";
|
|
15
|
+
const version = readMmaVersion();
|
|
30
16
|
export function createProgram() {
|
|
31
17
|
const program = new Command()
|
|
32
18
|
.name("mma")
|
|
@@ -50,12 +36,13 @@ export function createProgram() {
|
|
|
50
36
|
config.contextWindow = answers.contextWindow;
|
|
51
37
|
config.maxToolIterations = answers.maxToolIterations;
|
|
52
38
|
config.locale = answers.locale;
|
|
53
|
-
// Apply security settings from wizard
|
|
39
|
+
// Apply security settings from wizard.
|
|
40
|
+
// Security is enabled by default (balanced); wizard answers only tighten it.
|
|
54
41
|
if (config.security) {
|
|
55
|
-
config.security.enabled =
|
|
56
|
-
|
|
57
|
-
config.security.bash.
|
|
58
|
-
|
|
42
|
+
config.security.enabled = true;
|
|
43
|
+
config.security.bash.enabled = true;
|
|
44
|
+
config.security.bash.blockDangerousFlags =
|
|
45
|
+
answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
59
46
|
if (answers.securityBashBlock) {
|
|
60
47
|
config.security.bash.blacklist = [
|
|
61
48
|
...new Set([
|
|
@@ -83,7 +70,7 @@ export function createProgram() {
|
|
|
83
70
|
config.security.paths.denied = [];
|
|
84
71
|
}
|
|
85
72
|
}
|
|
86
|
-
saveConfig(config, configPath);
|
|
73
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
87
74
|
console.log(t("cli.config_saved"));
|
|
88
75
|
});
|
|
89
76
|
const configCmd = program.command("config").description(t("cli.manage_config"));
|
|
@@ -113,7 +100,7 @@ export function createProgram() {
|
|
|
113
100
|
obj[lastKey] = parseFloat(value);
|
|
114
101
|
else
|
|
115
102
|
obj[lastKey] = value;
|
|
116
|
-
saveConfig(config, configPath);
|
|
103
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
117
104
|
console.log(t("cli.set_done", { key, value }));
|
|
118
105
|
});
|
|
119
106
|
configCmd
|
|
@@ -123,6 +110,33 @@ export function createProgram() {
|
|
|
123
110
|
const { config } = await bootstrap();
|
|
124
111
|
console.log(JSON.stringify(config, null, 2));
|
|
125
112
|
});
|
|
113
|
+
configCmd
|
|
114
|
+
.command("migrate")
|
|
115
|
+
.description(t("cli.migrate_config"))
|
|
116
|
+
.action(async () => {
|
|
117
|
+
const { loadConfig } = await import("../config/config");
|
|
118
|
+
const { hasDomainFiles } = await import("../config/domains");
|
|
119
|
+
const configDir = join(homedir(), ".mma");
|
|
120
|
+
const configPath = join(configDir, "config.json");
|
|
121
|
+
if (hasDomainFiles(configDir)) {
|
|
122
|
+
console.log(pc.yellow(t("config.migrate_no_legacy")));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!existsSync(configPath)) {
|
|
126
|
+
console.log(pc.yellow(t("config.migrate_no_legacy")));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(t("config.migrate_start"));
|
|
130
|
+
const { config } = loadConfig({ configDir, projectConfigPath: join(configDir, ".mmrc") });
|
|
131
|
+
saveConfig(config, configPath, configDir);
|
|
132
|
+
// Rename legacy file
|
|
133
|
+
const bakPath = configPath + ".bak";
|
|
134
|
+
const { renameSync } = await import("fs");
|
|
135
|
+
renameSync(configPath, bakPath);
|
|
136
|
+
const { readdirSync } = await import("fs");
|
|
137
|
+
const domainFiles = readdirSync(join(configDir, "config")).filter((f) => f.endsWith(".json"));
|
|
138
|
+
console.log(pc.green(t("config.migrate_done", { count: String(domainFiles.length) })));
|
|
139
|
+
});
|
|
126
140
|
const model = program.command("model").description(t("cli.manage_models"));
|
|
127
141
|
model
|
|
128
142
|
.command("list")
|
|
@@ -131,8 +145,8 @@ export function createProgram() {
|
|
|
131
145
|
const { config } = await bootstrap();
|
|
132
146
|
console.log(t("cli.current_model"), config.model);
|
|
133
147
|
// Fetch available models from provider
|
|
134
|
-
const {
|
|
135
|
-
const provider =
|
|
148
|
+
const { createProvider } = await import("../modules/providers/create");
|
|
149
|
+
const provider = createProvider(config.provider.type, {
|
|
136
150
|
model: config.model,
|
|
137
151
|
baseUrl: config.provider.baseUrl,
|
|
138
152
|
apiKey: config.provider.apiKey,
|
|
@@ -149,7 +163,7 @@ export function createProgram() {
|
|
|
149
163
|
const { getCertMark } = await import("../modules/certification/manifest");
|
|
150
164
|
for (const m of models) {
|
|
151
165
|
const marker = m === config.model ? "* " : " ";
|
|
152
|
-
const mark = getCertMark(m, config.provider.baseUrl, version);
|
|
166
|
+
const mark = getCertMark(m, config.provider.baseUrl, version, process.cwd());
|
|
153
167
|
const cert = mark === "certified" ? "✔" : mark === "stale" ? "○" : "·";
|
|
154
168
|
console.log(` ${marker}${cert} ${m}`);
|
|
155
169
|
}
|
|
@@ -172,7 +186,7 @@ export function createProgram() {
|
|
|
172
186
|
const configPath = join(homedir(), ".mma", "config.json");
|
|
173
187
|
const { config } = await bootstrap();
|
|
174
188
|
config.model = name;
|
|
175
|
-
saveConfig(config, configPath);
|
|
189
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
176
190
|
console.log(t("cli.model_set", { name }));
|
|
177
191
|
});
|
|
178
192
|
model
|
|
@@ -199,6 +213,7 @@ export function createProgram() {
|
|
|
199
213
|
force: cmdOpts.force === true,
|
|
200
214
|
clean: cmdOpts.clean === true,
|
|
201
215
|
config,
|
|
216
|
+
projectDir: process.cwd(),
|
|
202
217
|
});
|
|
203
218
|
});
|
|
204
219
|
model
|
|
@@ -208,14 +223,14 @@ export function createProgram() {
|
|
|
208
223
|
.action(async (name) => {
|
|
209
224
|
const { config } = await bootstrap();
|
|
210
225
|
const { certStatus } = await import("../modules/certification/cli");
|
|
211
|
-
await certStatus(name, config);
|
|
226
|
+
await certStatus(name, config, process.cwd());
|
|
212
227
|
});
|
|
213
228
|
model
|
|
214
229
|
.command("cert-list")
|
|
215
230
|
.description(t("cli.cert_list"))
|
|
216
231
|
.action(async () => {
|
|
217
232
|
const { certList } = await import("../modules/certification/cli");
|
|
218
|
-
await certList();
|
|
233
|
+
await certList(process.cwd());
|
|
219
234
|
});
|
|
220
235
|
model
|
|
221
236
|
.command("uncertify")
|
|
@@ -224,7 +239,7 @@ export function createProgram() {
|
|
|
224
239
|
.action(async (name) => {
|
|
225
240
|
const { config } = await bootstrap();
|
|
226
241
|
const { uncertify } = await import("../modules/certification/cli");
|
|
227
|
-
await uncertify(name, config);
|
|
242
|
+
await uncertify(name, config, process.cwd());
|
|
228
243
|
});
|
|
229
244
|
// Context window command
|
|
230
245
|
program
|
|
@@ -240,15 +255,53 @@ export function createProgram() {
|
|
|
240
255
|
return;
|
|
241
256
|
}
|
|
242
257
|
config.contextWindow = contextWindow;
|
|
243
|
-
saveConfig(config, configPath);
|
|
258
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
244
259
|
console.log(t("cli.context_set", { size: contextWindow }));
|
|
245
260
|
});
|
|
246
261
|
const provider = program.command("provider").description(t("cli.manage_providers"));
|
|
262
|
+
provider
|
|
263
|
+
.command("check")
|
|
264
|
+
.description(t("cli.provider_check"))
|
|
265
|
+
.option("--timeout <ms>", "Per-provider probe timeout in ms")
|
|
266
|
+
.action(async (opts) => {
|
|
267
|
+
const { config, agent } = await bootstrap();
|
|
268
|
+
const providers = agent.listProviders();
|
|
269
|
+
const targets = targetsFromProviders(providers.length > 0
|
|
270
|
+
? providers
|
|
271
|
+
.map((p) => ({ name: p.label, provider: agent.getProviderFor(p.label) }))
|
|
272
|
+
.filter((x) => Boolean(x.provider))
|
|
273
|
+
: [
|
|
274
|
+
{
|
|
275
|
+
name: config.provider.type,
|
|
276
|
+
provider: agent.getProviderFor(config.provider.active || config.provider.type),
|
|
277
|
+
},
|
|
278
|
+
].filter((x) => Boolean(x.provider)));
|
|
279
|
+
const results = await probeProviders(targets, {
|
|
280
|
+
timeoutMs: opts.timeout ? Number(opts.timeout) : 5000,
|
|
281
|
+
});
|
|
282
|
+
for (const r of results) {
|
|
283
|
+
const mark = r.ok ? pc.green("✓") : pc.red("✗");
|
|
284
|
+
const detail = r.ok
|
|
285
|
+
? pc.dim(`${r.ms}ms, ${r.models} models`)
|
|
286
|
+
: pc.red(r.error ?? "failed");
|
|
287
|
+
console.log(` ${mark} ${r.name.padEnd(16)} ${detail}`);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
247
290
|
provider
|
|
248
291
|
.command("list")
|
|
249
292
|
.description(t("cli.list_providers"))
|
|
250
293
|
.action(async () => {
|
|
251
|
-
const { config } = await bootstrap();
|
|
294
|
+
const { config, agent } = await bootstrap();
|
|
295
|
+
const providers = agent.listProviders();
|
|
296
|
+
if (providers.length > 0) {
|
|
297
|
+
console.log(t("cli.current_provider"));
|
|
298
|
+
for (const p of providers) {
|
|
299
|
+
const marker = p.active ? pc.green("* ") : " ";
|
|
300
|
+
const prio = p.priority !== undefined ? ` ${pc.dim(`prio=${p.priority}`)}` : "";
|
|
301
|
+
console.log(` ${marker}${p.label} (${pc.dim(p.type)}) ${pc.dim(p.baseUrl)}${prio}`);
|
|
302
|
+
}
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
252
305
|
console.log(t("cli.current_provider"), config.provider.type);
|
|
253
306
|
console.log(t("cli.base_url"), config.provider.baseUrl);
|
|
254
307
|
});
|
|
@@ -258,10 +311,81 @@ export function createProgram() {
|
|
|
258
311
|
.description(t("cli.set_provider"))
|
|
259
312
|
.action(async (name) => {
|
|
260
313
|
const configPath = join(homedir(), ".mma", "config.json");
|
|
261
|
-
const { config } = await bootstrap();
|
|
314
|
+
const { config, agent } = await bootstrap();
|
|
315
|
+
if (config.provider.entries && config.provider.entries.length > 0) {
|
|
316
|
+
try {
|
|
317
|
+
await agent.setProvider(name);
|
|
318
|
+
console.log(t("cli.provider_set", { name }));
|
|
319
|
+
}
|
|
320
|
+
catch (e) {
|
|
321
|
+
console.log(pc.red(e.message));
|
|
322
|
+
}
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
262
325
|
config.provider.type = name;
|
|
263
|
-
|
|
326
|
+
const baseUrl = HOSTED_BASE_URLS[name];
|
|
327
|
+
if (baseUrl) {
|
|
328
|
+
config.provider.baseUrl = baseUrl;
|
|
329
|
+
}
|
|
330
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
264
331
|
console.log(t("cli.provider_set", { name }));
|
|
332
|
+
if (baseUrl) {
|
|
333
|
+
console.log(t("cli.provider_base_hint", { baseUrl }));
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
provider
|
|
337
|
+
.command("add")
|
|
338
|
+
.argument("<name>", "Provider type or label")
|
|
339
|
+
.option("--url <url>", "Base URL")
|
|
340
|
+
.option("--key <key>", "API key")
|
|
341
|
+
.option("--priority <n>", "Fallback priority (lower = tried first)")
|
|
342
|
+
.option("--context-window <n>", "Context window override for this entry")
|
|
343
|
+
.option("--rpm <n>", "Max requests per minute for this entry")
|
|
344
|
+
.option("--parallel <n>", "Max parallel tasks for this entry")
|
|
345
|
+
.description(t("cli.add_provider"))
|
|
346
|
+
.action(async (name, opts) => {
|
|
347
|
+
const configPath = join(homedir(), ".mma", "config.json");
|
|
348
|
+
const { config } = await bootstrap();
|
|
349
|
+
const entries = Array.isArray(config.provider.entries)
|
|
350
|
+
? config.provider.entries
|
|
351
|
+
: [];
|
|
352
|
+
// If a legacy single provider exists, seed the list with it first.
|
|
353
|
+
if (entries.length === 0) {
|
|
354
|
+
entries.push({
|
|
355
|
+
type: config.provider.type,
|
|
356
|
+
label: config.provider.type,
|
|
357
|
+
baseUrl: config.provider.baseUrl,
|
|
358
|
+
apiKey: config.provider.apiKey,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
const baseUrl = opts.url || HOSTED_BASE_URLS[name] || HOSTED_BASE_URLS[`opencode-${name}`] || "";
|
|
362
|
+
if (!baseUrl) {
|
|
363
|
+
console.log(pc.red(t("cli.provider_no_url", { name })));
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
entries.push({
|
|
367
|
+
type: name,
|
|
368
|
+
label: name,
|
|
369
|
+
baseUrl,
|
|
370
|
+
apiKey: opts.key,
|
|
371
|
+
...(opts.priority !== undefined ? { priority: Number(opts.priority) } : {}),
|
|
372
|
+
...(opts.contextWindow !== undefined
|
|
373
|
+
? { contextWindow: Number(opts.contextWindow) }
|
|
374
|
+
: {}),
|
|
375
|
+
...(opts.rpm !== undefined || opts.parallel !== undefined
|
|
376
|
+
? {
|
|
377
|
+
rateLimits: {
|
|
378
|
+
maxRequestsPerMinute: Number(opts.rpm ?? 0) || 60,
|
|
379
|
+
maxParallelTasks: Number(opts.parallel ?? 0) || 1,
|
|
380
|
+
},
|
|
381
|
+
}
|
|
382
|
+
: {}),
|
|
383
|
+
});
|
|
384
|
+
config.provider.entries = entries;
|
|
385
|
+
config.provider.active = config.provider.active || config.provider.type;
|
|
386
|
+
saveConfig(config, configPath, dirname(configPath));
|
|
387
|
+
console.log(pc.green(t("cli.provider_added", { name })));
|
|
388
|
+
console.log(t("cli.provider_switch_hint"));
|
|
265
389
|
});
|
|
266
390
|
const session = program.command("session").description(t("cli.manage_sessions"));
|
|
267
391
|
session
|
package/dist/cli/completer.js
CHANGED
|
@@ -30,15 +30,15 @@ export class SubcommandProvider {
|
|
|
30
30
|
}
|
|
31
31
|
export class ModelArgProvider {
|
|
32
32
|
name = "model-args";
|
|
33
|
-
|
|
34
|
-
constructor(
|
|
35
|
-
this.
|
|
33
|
+
getModels;
|
|
34
|
+
constructor(getModels) {
|
|
35
|
+
this.getModels = getModels;
|
|
36
36
|
}
|
|
37
37
|
match(ctx) {
|
|
38
|
-
return ctx.tokenIndex === 2 &&
|
|
38
|
+
return ctx.tokenIndex === 2 && ctx.tokens[0] === "/model" && ctx.tokens[1] === "use";
|
|
39
39
|
}
|
|
40
40
|
complete(ctx) {
|
|
41
|
-
return this.
|
|
41
|
+
return this.getModels().filter((m) => m.startsWith(ctx.partial));
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
export class ConfigKeyProvider {
|
package/dist/cli/main.js
CHANGED
|
@@ -4,35 +4,19 @@ import { Repl } from "./repl";
|
|
|
4
4
|
import { Renderer } from "../ui/renderer";
|
|
5
5
|
import { printRunResult } from "./run-result";
|
|
6
6
|
import { runSetup } from "./setup";
|
|
7
|
-
import { saveConfig
|
|
7
|
+
import { saveConfig } from "../config/config";
|
|
8
8
|
import { t } from "../i18n/index";
|
|
9
9
|
import { pc } from "../ui/colors";
|
|
10
|
-
import { existsSync
|
|
10
|
+
import { existsSync } from "fs";
|
|
11
11
|
import { join, dirname } from "path";
|
|
12
12
|
import { homedir } from "os";
|
|
13
|
-
import { fileURLToPath } from "url";
|
|
14
13
|
import { UpdaterModule } from "../modules/updater/index";
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const candidates = [join(here, "..", "..", "package.json"), join(here, "..", "package.json")];
|
|
18
|
-
for (const p of candidates) {
|
|
19
|
-
if (existsSync(p)) {
|
|
20
|
-
try {
|
|
21
|
-
const raw = JSON.parse(readFileSync(p, "utf8"));
|
|
22
|
-
if (raw.version)
|
|
23
|
-
return raw.version;
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
// fall through
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return "0.0.0";
|
|
31
|
-
}
|
|
14
|
+
import { installCrashHandlers } from "../core/crash-handler";
|
|
15
|
+
import { readMmaVersion } from "../core/version";
|
|
32
16
|
/** Fire-and-forget self-update in the background (never blocks startup). */
|
|
33
17
|
function startAutoUpdate(config) {
|
|
34
18
|
try {
|
|
35
|
-
const module = new UpdaterModule(config.updater,
|
|
19
|
+
const module = new UpdaterModule(config.updater, readMmaVersion(), "micro-models-agent", {
|
|
36
20
|
info: (m) => process.stderr.write(pc.dim(m) + "\n"),
|
|
37
21
|
warn: (m) => process.stderr.write(pc.yellow(m) + "\n"),
|
|
38
22
|
error: (m) => process.stderr.write(pc.red(m) + "\n"),
|
|
@@ -46,6 +30,9 @@ function startAutoUpdate(config) {
|
|
|
46
30
|
}
|
|
47
31
|
}
|
|
48
32
|
async function main() {
|
|
33
|
+
// Install global crash handlers first — every crash lands in
|
|
34
|
+
// ~/.mma/logs/crash.jsonl (with env report) instead of a bare stderr stack.
|
|
35
|
+
installCrashHandlers();
|
|
49
36
|
const program = createProgram();
|
|
50
37
|
program.parse(process.argv);
|
|
51
38
|
const cmdNames = new Set(program.commands.map((c) => c.name()));
|
|
@@ -60,7 +47,10 @@ async function main() {
|
|
|
60
47
|
}
|
|
61
48
|
if (program.args.length > 0) {
|
|
62
49
|
const prompt = program.args.join(" ");
|
|
63
|
-
const { agent, config } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
50
|
+
const { agent, config, baseDir, legacyDetected } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
51
|
+
if (legacyDetected) {
|
|
52
|
+
console.error(pc.yellow(` ${t("config.legacy_hint")}`));
|
|
53
|
+
}
|
|
64
54
|
const updater = exitOnComplete ? undefined : startAutoUpdate(config);
|
|
65
55
|
if (jsonMode) {
|
|
66
56
|
const result = await agent.run(prompt);
|
|
@@ -75,6 +65,8 @@ async function main() {
|
|
|
75
65
|
promptTokens: result.promptTokens ?? null,
|
|
76
66
|
completionTokens: result.completionTokens ?? null,
|
|
77
67
|
totalTokens: result.totalTokens ?? null,
|
|
68
|
+
totalCost: result.totalCost ?? null,
|
|
69
|
+
costBreakdown: result.costBreakdown ?? [],
|
|
78
70
|
}, null, 2));
|
|
79
71
|
process.stdout.write("\n");
|
|
80
72
|
await updater?.waitForIdle();
|
|
@@ -83,13 +75,14 @@ async function main() {
|
|
|
83
75
|
const renderer = new Renderer({
|
|
84
76
|
spinner: config.ui?.spinner ?? true,
|
|
85
77
|
toolStyle: config.ui?.toolStyle ?? "inline",
|
|
78
|
+
baseDir,
|
|
86
79
|
});
|
|
87
80
|
const result = await agent.run(prompt, (chunk) => renderer.text(chunk), (meta) => renderer.meta(meta), (ev) => {
|
|
88
81
|
if (ev.type === "start") {
|
|
89
|
-
renderer.toolStart(ev.tool, ev.args);
|
|
82
|
+
renderer.toolStart(ev.tool, ev.args, undefined, ev.icon);
|
|
90
83
|
}
|
|
91
84
|
else {
|
|
92
|
-
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta);
|
|
85
|
+
renderer.toolEnd(ev.tool, ev.duration ?? 0, ev.error, ev.ctxDelta, ev.costUsd);
|
|
93
86
|
}
|
|
94
87
|
}, (phase) => {
|
|
95
88
|
if (phase === "thinking") {
|
|
@@ -106,14 +99,23 @@ async function main() {
|
|
|
106
99
|
process.exit(exitCode);
|
|
107
100
|
}
|
|
108
101
|
else {
|
|
109
|
-
const
|
|
110
|
-
|
|
102
|
+
const mmaDir = join(homedir(), ".mma");
|
|
103
|
+
const legacyConfigPath = join(mmaDir, "config.json");
|
|
104
|
+
let hasAnyConfig = existsSync(legacyConfigPath);
|
|
105
|
+
if (!hasAnyConfig) {
|
|
106
|
+
try {
|
|
107
|
+
const { hasDomainFiles } = await import("../config/domains");
|
|
108
|
+
hasAnyConfig = hasDomainFiles(mmaDir);
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}
|
|
112
|
+
const { agent, config, sessionManager, skillsModule, pluginManager, execModule, configDir, baseDir, logger, envReport, } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
113
|
+
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true, envReport, undefined, execModule);
|
|
114
|
+
if (!hasAnyConfig) {
|
|
115
|
+
// First run: wizard via the REPL's LineEditor (avoids Bun stdin conflict
|
|
116
|
+
// from creating a second readline on process.stdin).
|
|
111
117
|
console.log(pc.yellow("\n " + t("cli.first_run") + "\n"));
|
|
112
|
-
const answers = await runSetup();
|
|
113
|
-
const config = loadConfig({
|
|
114
|
-
configDir: join(homedir(), ".mma"),
|
|
115
|
-
projectConfigPath: projectDir ? join(projectDir, ".mmrc") : join(process.cwd(), ".mmrc"),
|
|
116
|
-
});
|
|
118
|
+
const answers = await runSetup(repl.rl);
|
|
117
119
|
config.provider.type = answers.provider;
|
|
118
120
|
config.provider.baseUrl = answers.apiBase;
|
|
119
121
|
config.provider.apiKey = answers.apiKey;
|
|
@@ -122,30 +124,17 @@ async function main() {
|
|
|
122
124
|
config.maxToolIterations = answers.maxToolIterations;
|
|
123
125
|
config.locale = answers.locale;
|
|
124
126
|
if (config.security) {
|
|
125
|
-
config.security.enabled =
|
|
126
|
-
|
|
127
|
-
config.security.bash.
|
|
128
|
-
|
|
127
|
+
config.security.enabled = true;
|
|
128
|
+
config.security.bash.enabled = true;
|
|
129
|
+
config.security.bash.blockDangerousFlags =
|
|
130
|
+
answers.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
129
131
|
if (answers.securityBashBlock) {
|
|
130
132
|
config.security.bash.blacklist = [
|
|
131
133
|
...new Set([
|
|
132
134
|
...config.security.bash.blacklist,
|
|
133
|
-
"rm",
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"wget",
|
|
137
|
-
"curl",
|
|
138
|
-
"scp",
|
|
139
|
-
"ssh",
|
|
140
|
-
"nc",
|
|
141
|
-
"netcat",
|
|
142
|
-
"sudo",
|
|
143
|
-
"su",
|
|
144
|
-
"kill",
|
|
145
|
-
"pkill",
|
|
146
|
-
"killall",
|
|
147
|
-
"shutdown",
|
|
148
|
-
"reboot",
|
|
135
|
+
"rm", "dd", "chmod", "wget", "curl", "scp", "ssh",
|
|
136
|
+
"nc", "netcat", "sudo", "su", "kill", "pkill",
|
|
137
|
+
"killall", "shutdown", "reboot",
|
|
149
138
|
]),
|
|
150
139
|
];
|
|
151
140
|
}
|
|
@@ -153,11 +142,10 @@ async function main() {
|
|
|
153
142
|
config.security.paths.denied = [];
|
|
154
143
|
}
|
|
155
144
|
}
|
|
156
|
-
saveConfig(config,
|
|
145
|
+
saveConfig(config, legacyConfigPath, dirname(legacyConfigPath));
|
|
146
|
+
await agent.reconfigure(config);
|
|
157
147
|
}
|
|
158
|
-
const { agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, logger, } = await bootstrap(undefined, projectDir, noAgentsMd, exitOnComplete);
|
|
159
148
|
startAutoUpdate(config);
|
|
160
|
-
const repl = new Repl(agent, config, sessionManager, skillsModule, pluginManager, configDir, baseDir, noAgentsMd, logger, true);
|
|
161
149
|
await repl.start();
|
|
162
150
|
}
|
|
163
151
|
}
|