micro-models-agent 2.10.0 → 2.12.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 +33 -0
- package/dist/assets/app.js +22 -4
- package/dist/assets/index.html +29 -0
- package/dist/assets/js/dom.js +2 -1
- package/dist/assets/js/feed.js +38 -26
- package/dist/assets/js/format.js +1 -1
- package/dist/assets/js/rail.js +114 -0
- package/dist/assets/js/settings-controls.js +2 -2
- package/dist/assets/js/settings.js +246 -51
- package/dist/assets/js/store.js +3 -0
- package/dist/assets/js/tabs.js +104 -2
- package/dist/assets/styles.css +215 -66
- package/dist/cli/cache-line.js +1 -0
- package/dist/cli/command-suggest.js +5 -4
- package/dist/cli/commands.js +231 -122
- package/dist/cli/completer.js +132 -31
- package/dist/cli/index.js +1 -0
- package/dist/cli/json-payload.js +4 -4
- package/dist/cli/main.js +166 -61
- package/dist/cli/onboarding-commands.js +126 -0
- package/dist/cli/plugin-commands.js +2 -1
- package/dist/cli/relaunch.js +16 -13
- package/dist/cli/repl-commands-lsp.js +75 -0
- package/dist/cli/repl-commands-skills.js +105 -0
- package/dist/cli/repl-commands.js +589 -266
- package/dist/cli/repl.js +436 -177
- package/dist/cli/run-result.js +5 -6
- package/dist/cli/security-commands.js +37 -28
- package/dist/cli/setup-order.js +4 -55
- package/dist/cli/setup-prompt.js +3 -2
- package/dist/cli/setup.js +27 -107
- package/dist/cli/web-command.js +98 -0
- package/dist/config/budget.js +5 -5
- package/dist/config/config.js +103 -96
- package/dist/config/defaults.js +38 -8
- package/dist/config/domains.js +24 -27
- package/dist/config/env.js +46 -0
- package/dist/config/experts.js +2 -0
- package/dist/config/index.js +1 -0
- package/dist/config/paths.js +56 -0
- package/dist/config/redact.js +44 -0
- package/dist/config/registry-extra.js +334 -0
- package/dist/config/registry.js +584 -0
- package/dist/config/security.js +35 -36
- package/dist/config/timeouts.js +74 -0
- package/dist/config/utils.js +16 -15
- package/dist/core/agent/artifact-progress.js +59 -0
- package/dist/core/agent/artifact-resolve.js +79 -0
- package/dist/core/agent/audit-gate.js +7 -3
- package/dist/core/agent/compaction.js +2 -2
- package/dist/core/agent/constants.js +33 -4
- package/dist/core/agent/context-log.js +454 -0
- package/dist/core/agent/context-renderer.js +5 -0
- package/dist/core/agent/critic-gate.js +132 -0
- package/dist/core/agent/hallucination-gate.js +148 -38
- package/dist/core/agent/loop-state.js +15 -0
- package/dist/core/agent/prefix-monitor.js +1 -1
- package/dist/core/agent/reasoning-resolver.js +10 -1
- package/dist/core/agent/token-tracker.js +7 -4
- package/dist/core/agent/tool-batch.js +93 -18
- package/dist/core/agent/tool-images.js +18 -0
- package/dist/core/agent/tool-output.js +65 -11
- package/dist/core/agent/tool-pairing.js +60 -0
- package/dist/core/agent-moe.js +25 -24
- package/dist/core/agent.js +429 -108
- package/dist/core/bootstrap/agent-phase.js +66 -0
- package/dist/core/bootstrap/config-phase.js +86 -0
- package/dist/core/bootstrap/modules-phase.js +269 -0
- package/dist/core/bootstrap/provider-phase.js +183 -0
- package/dist/core/bootstrap/session-phase.js +204 -0
- package/dist/core/bootstrap/state.js +94 -0
- package/dist/core/bootstrap/workspace-phase.js +115 -0
- package/dist/core/bootstrap.js +92 -660
- package/dist/core/crash-handler.js +17 -7
- package/dist/core/environment.js +37 -12
- package/dist/core/host-plan.js +43 -0
- package/dist/core/host.js +23 -0
- package/dist/core/index.js +1 -0
- package/dist/core/prompt-builder.js +17 -1
- package/dist/core/prompt-overflow.js +42 -43
- package/dist/core/session-logger.js +122 -30
- package/dist/core/system-prompt.js +117 -0
- package/dist/core/version.js +6 -4
- package/dist/core/web-host.js +34 -0
- package/dist/core/workspace.js +19 -15
- package/dist/i18n/en.json +106 -21
- package/dist/i18n/index.js +37 -3
- package/dist/i18n/ru.json +109 -21
- package/dist/index.js +7 -3
- package/dist/llm/image-utils.js +55 -64
- package/dist/llm/llm-errors.js +9 -0
- package/dist/llm/model-loader.js +10 -32
- package/dist/llm/models-dev.js +121 -0
- package/dist/llm/openai-compat.js +146 -73
- package/dist/llm/orchestrator.js +33 -11
- package/dist/llm/provider-budget.js +2 -2
- package/dist/llm/provider-context.js +233 -0
- package/dist/llm/provider.js +1 -1
- package/dist/llm/response.js +5 -0
- package/dist/llm/token-counter.js +27 -11
- package/dist/logger/app-logger.js +48 -22
- package/dist/logger/file-log.js +28 -19
- package/dist/logger/index.js +1 -0
- package/dist/main.js +24285 -22447
- package/dist/migration/backup.js +14 -3
- package/dist/migration/detect.js +12 -3
- package/dist/migration/index.js +1 -0
- package/dist/modules/artifacts/store.js +9 -8
- package/dist/modules/browser/actions.js +6 -0
- package/dist/modules/browser/bridge-client.js +14 -12
- package/dist/modules/browser/bridge-path.js +4 -4
- package/dist/modules/browser/console-buffer.js +37 -0
- package/dist/modules/browser/cookie-store.js +5 -0
- package/dist/modules/browser/driver-types.js +1 -0
- package/dist/modules/browser/driver.js +19 -11
- package/dist/modules/browser/index.js +3 -1
- package/dist/modules/browser/module.js +2 -1
- package/dist/modules/browser/session.js +10 -30
- package/dist/modules/browser/snapshot.js +19 -13
- package/dist/modules/browser/types.js +1 -0
- package/dist/modules/certification/cli.js +28 -20
- package/dist/modules/certification/fact-checker.js +9 -1
- package/dist/modules/certification/loader.js +9 -2
- package/dist/modules/certification/manifest.js +83 -21
- package/dist/modules/certification/runner.js +23 -13
- package/dist/modules/certification/scenarios.js +20 -20
- package/dist/modules/certification/syntax-scenarios.js +1 -2
- package/dist/modules/context/chunk-query.js +11 -9
- package/dist/modules/context/fact-extractor.js +37 -39
- package/dist/modules/context/index.js +1 -0
- package/dist/modules/context/manager.js +106 -80
- package/dist/modules/critic/client.js +176 -0
- package/dist/modules/critic/evidence.js +81 -0
- package/dist/modules/critic/provider.js +45 -0
- package/dist/modules/critic/tools.js +78 -0
- package/dist/modules/critic/types.js +1 -0
- package/dist/modules/execution/audit-runners.js +28 -34
- package/dist/modules/execution/auditor.js +53 -49
- package/dist/modules/execution/execution-plugin.js +142 -102
- package/dist/modules/execution/index.js +1 -1
- package/dist/modules/execution/input-from.js +37 -13
- package/dist/modules/execution/module.js +368 -191
- package/dist/modules/execution/moe-executor.js +53 -26
- package/dist/modules/execution/plan-actions.js +320 -202
- package/dist/modules/execution/plan-checks.js +35 -0
- package/dist/modules/execution/plan-coverage.js +36 -33
- package/dist/modules/execution/plan-steps.js +11 -3
- package/dist/modules/execution/plan-store.js +83 -17
- package/dist/modules/execution/plan-tool.js +89 -35
- package/dist/modules/execution/plan-validator.js +30 -10
- package/dist/modules/execution/planner.js +22 -13
- package/dist/modules/execution/stuck-detector.js +196 -93
- package/dist/modules/execution/tracker.js +22 -8
- package/dist/modules/execution/verifier.js +69 -48
- package/dist/modules/execution/windows-commands.js +12 -12
- package/dist/modules/hallucination/artifact-claim.js +93 -0
- package/dist/modules/hallucination/confidence.js +13 -11
- package/dist/modules/hallucination/consistency.js +10 -2
- package/dist/modules/hallucination/detector.js +10 -5
- package/dist/modules/hallucination/factual.js +43 -26
- package/dist/modules/hallucination/index.js +1 -0
- package/dist/modules/hallucination/js-identifiers.js +44 -45
- package/dist/modules/hallucination/llm-judge.js +17 -12
- package/dist/modules/hallucination/success-claim.js +59 -0
- package/dist/modules/indexer/cache.js +52 -4
- package/dist/modules/indexer/index.js +1 -0
- package/dist/modules/indexer/map-command.js +2 -4
- package/dist/modules/indexer/map-select.js +16 -16
- package/dist/modules/indexer/module.js +47 -12
- package/dist/modules/indexer/project-profile.js +8 -7
- package/dist/modules/indexer/symbols.js +15 -15
- package/dist/modules/indexer/walker.js +159 -29
- package/dist/modules/lsp/check-tool.js +7 -5
- package/dist/modules/lsp/client.js +39 -39
- package/dist/modules/lsp/command.js +17 -24
- package/dist/modules/lsp/config.js +12 -11
- package/dist/modules/lsp/index.js +1 -0
- package/dist/modules/lsp/module.js +48 -46
- package/dist/modules/lsp/probe.js +13 -14
- package/dist/modules/lsp/project-root.js +14 -17
- package/dist/modules/lsp/startup-check.js +18 -16
- package/dist/modules/mcp/client.js +59 -62
- package/dist/modules/mcp/index.js +1 -0
- package/dist/modules/mcp/module.js +66 -37
- package/dist/modules/mcp/registry.js +5 -0
- package/dist/modules/memory/index.js +1 -0
- package/dist/modules/memory/module.js +12 -8
- package/dist/modules/memory/search.js +4 -1
- package/dist/modules/memory/store.js +40 -2
- package/dist/modules/onboarding/init.js +47 -0
- package/dist/modules/onboarding/meet.js +30 -0
- package/dist/modules/onboarding/mode.js +30 -0
- package/dist/modules/onboarding/prompts.js +43 -0
- package/dist/modules/pipelines/engine.js +10 -0
- package/dist/modules/pipelines/index.js +1 -0
- package/dist/modules/pipelines/parser.js +4 -1
- package/dist/modules/pipelines/template.js +4 -0
- package/dist/modules/plugins/builtin/lint-on-write.js +80 -67
- package/dist/modules/plugins/builtin/notify.js +2 -0
- package/dist/modules/plugins/index.js +1 -0
- package/dist/modules/plugins/loader.js +18 -3
- package/dist/modules/plugins/manager.js +37 -17
- package/dist/modules/pricing/index.js +10 -11
- package/dist/modules/pricing/prices.js +12 -12
- package/dist/modules/processes/index.js +1 -0
- package/dist/modules/processes/kill-tree.js +27 -31
- package/dist/modules/processes/registry.js +44 -25
- package/dist/modules/processes/runner.js +4 -4
- package/dist/modules/providers/capabilities.js +28 -0
- package/dist/modules/providers/create.js +3 -4
- package/dist/modules/providers/factory.js +7 -7
- package/dist/modules/providers/fallback.js +12 -12
- package/dist/modules/providers/health.js +4 -4
- package/dist/modules/providers/manager.js +50 -19
- package/dist/modules/providers/naming.js +15 -0
- package/dist/modules/providers/preflight.js +71 -0
- package/dist/modules/providers/presets.js +39 -8
- package/dist/modules/providers/registry.js +7 -2
- package/dist/modules/reasoning/policy.js +8 -6
- package/dist/modules/reasoning/probe.js +30 -25
- package/dist/modules/security/audit-log.js +19 -36
- package/dist/modules/security/audit-notifier.js +35 -75
- package/dist/modules/security/command-validator.js +99 -51
- package/dist/modules/security/content-scanner.js +8 -8
- package/dist/modules/security/data-sanitizer.js +9 -16
- package/dist/modules/security/encryption.js +32 -52
- package/dist/modules/security/index.js +2 -1
- package/dist/modules/security/network-validator.js +120 -43
- package/dist/modules/security/path-validator.js +61 -46
- package/dist/modules/security/protected-paths.js +73 -0
- package/dist/modules/security/rate-limiter.js +17 -49
- package/dist/modules/security/security-policies.js +25 -57
- package/dist/modules/security/session-encryption.js +32 -71
- package/dist/modules/security/session-isolation.js +15 -28
- package/dist/modules/session/auto-name.js +63 -0
- package/dist/modules/session/export.js +98 -0
- package/dist/modules/session/index.js +1 -0
- package/dist/modules/session/manager.js +79 -28
- package/dist/modules/session/module.js +4 -3
- package/dist/modules/session/store.js +57 -20
- package/dist/modules/setup/answers.js +35 -0
- package/dist/modules/setup/apply.js +79 -0
- package/dist/modules/setup/discovery.js +72 -0
- package/dist/modules/skills/index.js +1 -0
- package/dist/modules/skills/loader.js +9 -1
- package/dist/modules/skills/module.js +11 -0
- package/dist/modules/updater/changelog-reader.js +15 -16
- package/dist/modules/updater/checker.js +9 -4
- package/dist/modules/updater/dev-detect.js +7 -6
- package/dist/modules/updater/index.js +1 -0
- package/dist/modules/updater/module.js +17 -15
- package/dist/modules/user-profile/compressor.js +2 -0
- package/dist/modules/user-profile/index.js +1 -0
- package/dist/modules/user-profile/profile.js +10 -1
- package/dist/modules/webui/answer-api.js +29 -0
- package/dist/modules/webui/assets.js +50 -0
- package/dist/modules/webui/body.js +21 -0
- package/dist/modules/webui/commands-settings.js +209 -0
- package/dist/modules/webui/commands-shared.js +60 -0
- package/dist/modules/webui/commands.js +174 -0
- package/dist/modules/webui/config-api.js +134 -0
- package/dist/modules/webui/file-api.js +202 -0
- package/dist/modules/webui/forward.js +159 -0
- package/dist/modules/webui/history-api.js +112 -0
- package/dist/modules/webui/host-state.js +60 -0
- package/dist/modules/webui/i18n-api.js +9 -0
- package/dist/modules/webui/limits.js +6 -0
- package/dist/modules/webui/log-forward.js +20 -0
- package/dist/modules/webui/memory-commands.js +64 -0
- package/dist/modules/webui/model-cache.js +18 -0
- package/dist/modules/webui/module.js +148 -0
- package/dist/modules/webui/persist.js +24 -0
- package/dist/modules/webui/prompt-io.js +97 -0
- package/dist/modules/webui/providers-api.js +337 -0
- package/dist/modules/webui/providers-view.js +56 -0
- package/dist/modules/webui/reasoning-view.js +15 -0
- package/dist/modules/webui/run-agent.js +81 -0
- package/dist/modules/webui/server.js +284 -0
- package/dist/modules/webui/sessions-api.js +178 -0
- package/dist/modules/webui/settings-api.js +561 -0
- package/dist/modules/webui/setup-api.js +201 -0
- package/dist/modules/webui/state.js +74 -0
- package/dist/modules/webui/util.js +261 -0
- package/dist/output/bus.js +7 -2
- package/dist/output/channel.js +80 -28
- package/dist/output/format.js +2 -1
- package/dist/output/index.js +1 -0
- package/dist/output/json-sink.js +5 -3
- package/dist/output/machine.js +4 -3
- package/dist/output/session-sink.js +4 -4
- package/dist/tools/approve.js +4 -0
- package/dist/tools/attach-image.js +5 -4
- package/dist/tools/auto-fixer.js +26 -44
- package/dist/tools/auto-tags.js +83 -0
- package/dist/tools/bash.js +193 -116
- package/dist/tools/browser.js +76 -31
- package/dist/tools/chunk-query.js +14 -6
- package/dist/tools/create-dir.js +9 -5
- package/dist/tools/delete-file.js +9 -5
- package/dist/tools/download-file.js +32 -7
- package/dist/tools/edit-file.js +52 -14
- package/dist/tools/enable-tools.js +6 -6
- package/dist/tools/executor.js +132 -27
- package/dist/tools/file-info.js +3 -1
- package/dist/tools/filter-tools.js +3 -3
- package/dist/tools/glob-tool.js +4 -1
- package/dist/tools/grep-tool.js +13 -10
- package/dist/tools/hidden-tools-block.js +6 -6
- package/dist/tools/index.js +16 -8
- package/dist/tools/list-dir.js +4 -2
- package/dist/tools/load-skill.js +7 -0
- package/dist/tools/mcp-call.js +15 -4
- package/dist/tools/meet-done.js +14 -0
- package/dist/tools/move-file.js +14 -7
- package/dist/tools/path-utils.js +29 -29
- package/dist/tools/pipeline-run.js +29 -7
- package/dist/tools/preview.js +1 -1
- package/dist/tools/process-ids.js +31 -0
- package/dist/tools/process-kill.js +9 -16
- package/dist/tools/process-list.js +1 -0
- package/dist/tools/process-log.js +11 -16
- package/dist/tools/process-wait.js +63 -0
- package/dist/tools/prompt-io.js +4 -3
- package/dist/tools/question.js +172 -25
- package/dist/tools/read-file.js +55 -34
- package/dist/tools/recall.js +14 -8
- package/dist/tools/registry.js +6 -5
- package/dist/tools/remember.js +8 -4
- package/dist/tools/scope-request.js +11 -6
- package/dist/tools/search-history.js +5 -2
- package/dist/tools/session-info.js +2 -0
- package/dist/tools/set-thinking.js +8 -4
- package/dist/tools/subagent.js +59 -39
- package/dist/tools/syntax-validator.js +292 -90
- package/dist/tools/user-input.js +22 -18
- package/dist/tools/web-browse.js +9 -3
- package/dist/tools/web-fetch.js +12 -23
- package/dist/tools/web-search.js +7 -5
- package/dist/tools/write-file.js +45 -15
- package/dist/ui/action-log.js +13 -0
- package/dist/ui/box.js +4 -3
- package/dist/ui/colors.js +2 -0
- package/dist/ui/diff.js +6 -5
- package/dist/ui/index.js +1 -0
- package/dist/ui/line-editor.js +230 -122
- package/dist/ui/line-math.js +10 -2
- package/dist/ui/md-formatter.js +57 -37
- package/dist/ui/output.js +8 -3
- package/dist/ui/plan-view.js +21 -10
- package/dist/ui/renderer.js +393 -67
- package/dist/ui/spinner.js +16 -5
- package/dist/ui/table.js +15 -17
- package/dist/ui/theme.js +143 -0
- package/dist/utils/child-run.js +20 -0
- package/dist/utils/error.js +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/retry.js +2 -2
- package/dist/utils/truncate.js +1 -1
- package/package.json +1 -1
package/dist/cli/run-result.js
CHANGED
|
@@ -3,14 +3,13 @@ import { pc } from "../ui/colors";
|
|
|
3
3
|
import { formatCost } from "../modules/pricing/prices";
|
|
4
4
|
import { formatCacheLine } from "./cache-line";
|
|
5
5
|
import { getDefaultChannel } from "../output";
|
|
6
|
+
// Печать результата one-shot прогона: стоимость/токены/кеш, код выхода.
|
|
6
7
|
/**
|
|
7
|
-
*
|
|
8
|
-
* the process exit code.
|
|
8
|
+
* Рендерит one-shot AgentResult в терминал (не-JSON режим) и возвращает код выхода.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* suggests the task succeeded.
|
|
10
|
+
* При провале печатается только ошибка. `text` модели (последнее *принятое*
|
|
11
|
+
* сообщение — возможно, протухшее после ошибки аудита/галлюцинаций) уже
|
|
12
|
+
* стримлено живьём во время прогона: допечатка здесь ложно намекнула бы на успех.
|
|
14
13
|
*/
|
|
15
14
|
export function printRunResult(result, flush, channel = getDefaultChannel()) {
|
|
16
15
|
flush();
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { bootstrap } from "../core/bootstrap";
|
|
2
2
|
import { saveConfig } from "../config/config";
|
|
3
|
-
import {
|
|
4
|
-
import { homedir } from "os";
|
|
3
|
+
import { dirname } from "path";
|
|
5
4
|
import { SECURITY_POLICIES, applySecurityPolicy, getSecurityPolicy, } from "../modules/security/security-policies";
|
|
6
5
|
import { globalAuditNotifier } from "../modules/security/audit-notifier";
|
|
6
|
+
import { DEFAULT_SECURITY_CONFIG } from "../config/security";
|
|
7
7
|
import { t } from "../i18n/index";
|
|
8
8
|
import { getDefaultChannel } from "../output";
|
|
9
|
+
import { globalConfigFile } from "../config/paths";
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* Переключает шифрование файлов сессии на месте: флипает `enabled` (и оба
|
|
12
|
+
* пофайловых флага), СОХРАНЯЯ уже лежащие в конфиге юзер-поля (keyPath и др.).
|
|
13
|
+
* Мутирует переданный объект security.
|
|
13
14
|
*/
|
|
14
15
|
export function toggleSessionEncryption(security, enabled) {
|
|
15
16
|
const enc = (security.sessionEncryption ?? {});
|
|
@@ -19,8 +20,8 @@ export function toggleSessionEncryption(security, enabled) {
|
|
|
19
20
|
security.sessionEncryption = enc;
|
|
20
21
|
}
|
|
21
22
|
/**
|
|
22
|
-
*
|
|
23
|
-
* webhookUrl / filePath / minSeverity / eventTypes
|
|
23
|
+
* Переключает аудит-нотификации на месте: флипает только `enabled`, сохраняя
|
|
24
|
+
* юзерские webhookUrl / filePath / minSeverity / eventTypes.
|
|
24
25
|
*/
|
|
25
26
|
export function toggleAuditNotifier(security, enabled) {
|
|
26
27
|
const notifier = (security.auditNotifier ?? {});
|
|
@@ -28,17 +29,17 @@ export function toggleAuditNotifier(security, enabled) {
|
|
|
28
29
|
security.auditNotifier = notifier;
|
|
29
30
|
}
|
|
30
31
|
/**
|
|
31
|
-
*
|
|
32
|
+
* Создаёт субкоманду security.
|
|
32
33
|
*/
|
|
33
34
|
export function createSecurityCommand(program) {
|
|
34
35
|
const securityCmd = program.command("security").description(t("cli.security.description"));
|
|
35
|
-
// security status
|
|
36
|
+
// security status — текущий конфиг безопасности.
|
|
36
37
|
securityCmd
|
|
37
38
|
.command("status")
|
|
38
39
|
.description(t("cli.security.status"))
|
|
39
40
|
.action(async () => {
|
|
40
41
|
const { config } = await bootstrap();
|
|
41
|
-
const security = config.security
|
|
42
|
+
const security = config.security ?? DEFAULT_SECURITY_CONFIG;
|
|
42
43
|
const bash = security.bash || {};
|
|
43
44
|
const paths = security.paths || {};
|
|
44
45
|
const network = security.network || {};
|
|
@@ -48,7 +49,7 @@ export function createSecurityCommand(program) {
|
|
|
48
49
|
const auditNotifier = security.auditNotifier || {};
|
|
49
50
|
getDefaultChannel().writeLine(t("cli.security.current_policy"));
|
|
50
51
|
getDefaultChannel().writeLine(` ${t("cli.security.bash_enabled")}: ${bash.blacklist?.length > 0 ? t("cli.yes") : t("cli.no")}`);
|
|
51
|
-
getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.
|
|
52
|
+
getDefaultChannel().writeLine(` ${t("cli.security.path_validation")}: ${paths.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
52
53
|
getDefaultChannel().writeLine(` ${t("cli.security.network_validation")}: ${network.deniedDomains?.length > 0 || network.allowedDomains?.length > 0 ? t("cli.yes") : t("cli.no")}`);
|
|
53
54
|
getDefaultChannel().writeLine(` ${t("cli.security.content_scanning")}: ${contentScan.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
54
55
|
getDefaultChannel().writeLine(` ${t("cli.security.max_recursion")}: ${security.maxRecursionDepth ?? 3}`);
|
|
@@ -57,7 +58,7 @@ export function createSecurityCommand(program) {
|
|
|
57
58
|
getDefaultChannel().writeLine(` ${t("cli.security.session_encryption")}: ${sessionEncryption.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
58
59
|
getDefaultChannel().writeLine(` ${t("cli.security.audit_notifier")}: ${auditNotifier.enabled ? t("cli.yes") : t("cli.no")}`);
|
|
59
60
|
});
|
|
60
|
-
// security policies
|
|
61
|
+
// security policies — список политик.
|
|
61
62
|
securityCmd
|
|
62
63
|
.command("policies")
|
|
63
64
|
.description(t("cli.security.policies"))
|
|
@@ -74,13 +75,13 @@ export function createSecurityCommand(program) {
|
|
|
74
75
|
getDefaultChannel().writeLine("");
|
|
75
76
|
}
|
|
76
77
|
});
|
|
77
|
-
// security set-policy
|
|
78
|
+
// security set-policy — применить политику.
|
|
78
79
|
securityCmd
|
|
79
80
|
.command("set-policy")
|
|
80
81
|
.argument("<preset>", t("cli.security.preset"))
|
|
81
82
|
.description(t("cli.security.set_policy"))
|
|
82
83
|
.action(async (preset) => {
|
|
83
|
-
const configPath =
|
|
84
|
+
const configPath = globalConfigFile();
|
|
84
85
|
const { config: appConfig } = await bootstrap();
|
|
85
86
|
const validPresets = ["strict", "balanced", "permissive"];
|
|
86
87
|
if (!validPresets.includes(preset)) {
|
|
@@ -89,61 +90,69 @@ export function createSecurityCommand(program) {
|
|
|
89
90
|
}
|
|
90
91
|
const policy = getSecurityPolicy(preset);
|
|
91
92
|
const newSecurityConfig = applySecurityPolicy(preset);
|
|
92
|
-
//
|
|
93
|
+
// Мерж с существующим конфигом.
|
|
93
94
|
appConfig.security = newSecurityConfig;
|
|
94
95
|
saveConfig(appConfig, configPath, dirname(configPath));
|
|
95
96
|
getDefaultChannel().writeLine(t("cli.security.policy_applied", { name: policy.name }));
|
|
96
97
|
getDefaultChannel().writeLine(t("cli.security.policy_description", { description: policy.description }));
|
|
97
98
|
});
|
|
98
|
-
// security enable-encryption
|
|
99
|
+
// security enable-encryption — включить шифрование файлов сессии.
|
|
99
100
|
securityCmd
|
|
100
101
|
.command("enable-encryption")
|
|
101
102
|
.description(t("cli.security.enable_encryption"))
|
|
102
103
|
.action(async () => {
|
|
103
|
-
const configPath =
|
|
104
|
+
const configPath = globalConfigFile();
|
|
104
105
|
const { config: appConfig } = await bootstrap();
|
|
105
|
-
|
|
106
|
+
if (!appConfig.security)
|
|
107
|
+
appConfig.security = structuredClone(DEFAULT_SECURITY_CONFIG);
|
|
108
|
+
const security = appConfig.security;
|
|
106
109
|
toggleSessionEncryption(security, true);
|
|
107
110
|
saveConfig(appConfig, configPath, dirname(configPath));
|
|
108
111
|
getDefaultChannel().writeLine(t("cli.security.encryption_enabled"));
|
|
109
112
|
});
|
|
110
|
-
// security disable-encryption
|
|
113
|
+
// security disable-encryption — выключить шифрование файлов сессии.
|
|
111
114
|
securityCmd
|
|
112
115
|
.command("disable-encryption")
|
|
113
116
|
.description(t("cli.security.disable_encryption"))
|
|
114
117
|
.action(async () => {
|
|
115
|
-
const configPath =
|
|
118
|
+
const configPath = globalConfigFile();
|
|
116
119
|
const { config: appConfig } = await bootstrap();
|
|
117
|
-
|
|
120
|
+
if (!appConfig.security)
|
|
121
|
+
appConfig.security = structuredClone(DEFAULT_SECURITY_CONFIG);
|
|
122
|
+
const security = appConfig.security;
|
|
118
123
|
toggleSessionEncryption(security, false);
|
|
119
124
|
saveConfig(appConfig, configPath, dirname(configPath));
|
|
120
125
|
getDefaultChannel().writeLine(t("cli.security.encryption_disabled"));
|
|
121
126
|
});
|
|
122
|
-
// security enable-audit
|
|
127
|
+
// security enable-audit — включить аудит-нотификации.
|
|
123
128
|
securityCmd
|
|
124
129
|
.command("enable-audit")
|
|
125
130
|
.description(t("cli.security.enable_audit"))
|
|
126
131
|
.action(async () => {
|
|
127
|
-
const configPath =
|
|
132
|
+
const configPath = globalConfigFile();
|
|
128
133
|
const { config: appConfig } = await bootstrap();
|
|
129
|
-
|
|
134
|
+
if (!appConfig.security)
|
|
135
|
+
appConfig.security = structuredClone(DEFAULT_SECURITY_CONFIG);
|
|
136
|
+
const security = appConfig.security;
|
|
130
137
|
toggleAuditNotifier(security, true);
|
|
131
138
|
saveConfig(appConfig, configPath, dirname(configPath));
|
|
132
139
|
getDefaultChannel().writeLine(t("cli.security.audit_enabled"));
|
|
133
140
|
});
|
|
134
|
-
// security disable-audit
|
|
141
|
+
// security disable-audit — выключить аудит-нотификации.
|
|
135
142
|
securityCmd
|
|
136
143
|
.command("disable-audit")
|
|
137
144
|
.description(t("cli.security.disable_audit"))
|
|
138
145
|
.action(async () => {
|
|
139
|
-
const configPath =
|
|
146
|
+
const configPath = globalConfigFile();
|
|
140
147
|
const { config: appConfig } = await bootstrap();
|
|
141
|
-
|
|
148
|
+
if (!appConfig.security)
|
|
149
|
+
appConfig.security = structuredClone(DEFAULT_SECURITY_CONFIG);
|
|
150
|
+
const security = appConfig.security;
|
|
142
151
|
toggleAuditNotifier(security, false);
|
|
143
152
|
saveConfig(appConfig, configPath, dirname(configPath));
|
|
144
153
|
getDefaultChannel().writeLine(t("cli.security.audit_disabled"));
|
|
145
154
|
});
|
|
146
|
-
// security audit-stats
|
|
155
|
+
// security audit-stats — статистика аудит-нотификаций.
|
|
147
156
|
securityCmd
|
|
148
157
|
.command("audit-stats")
|
|
149
158
|
.description(t("cli.security.audit_stats"))
|
package/dist/cli/setup-order.js
CHANGED
|
@@ -1,57 +1,6 @@
|
|
|
1
|
-
/** Extra commands added to the bash blacklist when the wizard enables it. */
|
|
2
|
-
const SECURITY_BASH_BLOCKLIST = [
|
|
3
|
-
"rm",
|
|
4
|
-
"dd",
|
|
5
|
-
"chmod",
|
|
6
|
-
"wget",
|
|
7
|
-
"curl",
|
|
8
|
-
"scp",
|
|
9
|
-
"ssh",
|
|
10
|
-
"nc",
|
|
11
|
-
"netcat",
|
|
12
|
-
"sudo",
|
|
13
|
-
"su",
|
|
14
|
-
"kill",
|
|
15
|
-
"pkill",
|
|
16
|
-
"killall",
|
|
17
|
-
"shutdown",
|
|
18
|
-
"reboot",
|
|
19
|
-
];
|
|
20
1
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* place: `loadConfig` may register a session-scoped `MMA_PROVIDER_*` env
|
|
25
|
-
* override whose `envValue` points at the loaded `config.provider` object.
|
|
26
|
-
* `saveConfig`'s `stripEnvOverrides` then matches that object by identity and
|
|
27
|
-
* would revert the whole provider to the env/default value, silently losing
|
|
28
|
-
* the wizard's provider/URL/key. Replacing the object keeps the override's
|
|
29
|
-
* stale identity intact, so only the env key is stripped and our answers are
|
|
30
|
-
* persisted.
|
|
2
|
+
* Шим совместимости: логика записи конфига визарда живёт в
|
|
3
|
+
* `src/modules/setup/apply.ts`, чтобы её делил браузерный визард. Реэкспорт
|
|
4
|
+
* отсюда сохраняет терминальный путь импорта (и его тесты).
|
|
31
5
|
*/
|
|
32
|
-
export
|
|
33
|
-
config.provider = {
|
|
34
|
-
...config.provider,
|
|
35
|
-
type: a.provider,
|
|
36
|
-
baseUrl: a.apiBase,
|
|
37
|
-
apiKey: a.apiKey,
|
|
38
|
-
};
|
|
39
|
-
config.model = a.model;
|
|
40
|
-
config.contextWindow = a.contextWindow;
|
|
41
|
-
config.maxToolIterations = a.maxToolIterations;
|
|
42
|
-
config.locale = a.locale;
|
|
43
|
-
if (config.security) {
|
|
44
|
-
config.security.enabled = true;
|
|
45
|
-
config.security.bash.enabled = true;
|
|
46
|
-
config.security.bash.blockDangerousFlags =
|
|
47
|
-
a.securityFlagsBlock || config.security.bash.blockDangerousFlags;
|
|
48
|
-
if (a.securityBashBlock) {
|
|
49
|
-
config.security.bash.blacklist = [
|
|
50
|
-
...new Set([...config.security.bash.blacklist, ...SECURITY_BASH_BLOCKLIST]),
|
|
51
|
-
];
|
|
52
|
-
}
|
|
53
|
-
if (!a.securityPathsDeny)
|
|
54
|
-
config.security.paths.denied = [];
|
|
55
|
-
}
|
|
56
|
-
return config;
|
|
57
|
-
}
|
|
6
|
+
export { applyAnswersToConfig, SECURITY_BASH_BLOCKLIST } from "../modules/setup/apply";
|
package/dist/cli/setup-prompt.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Cooked-режимный промпт визарда: эхо/правка на терминале, мы копим до newline. */
|
|
1
2
|
export class SetupPrompt {
|
|
2
3
|
input;
|
|
3
4
|
output;
|
|
@@ -8,7 +9,7 @@ export class SetupPrompt {
|
|
|
8
9
|
constructor(input = process.stdin, output = process.stdout) {
|
|
9
10
|
this.input = input;
|
|
10
11
|
this.output = output;
|
|
11
|
-
// Cooked
|
|
12
|
+
// Cooked-режим: правка строки и эхо — на консоли.
|
|
12
13
|
if (typeof this.input.setRawMode === "function")
|
|
13
14
|
this.input.setRawMode(false);
|
|
14
15
|
this.onData = (buf) => this.consume(buf.toString("utf-8"));
|
|
@@ -41,7 +42,7 @@ export class SetupPrompt {
|
|
|
41
42
|
this.buffer = "";
|
|
42
43
|
const cb = this.pending;
|
|
43
44
|
this.pending = null;
|
|
44
|
-
//
|
|
45
|
+
// TTY сам эхует Enter переводом строки; дописываем его только пайпам.
|
|
45
46
|
if (!this.output.isTTY)
|
|
46
47
|
this.output.write("\n");
|
|
47
48
|
cb(answer);
|
package/dist/cli/setup.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
|
+
// Визард первого запуска: язык → провайдер → ключ → модель → контекст → security.
|
|
1
2
|
import { pc } from "../ui/colors";
|
|
2
3
|
import { t, setLocale } from "../i18n/index";
|
|
3
4
|
import { Spinner } from "../ui/spinner";
|
|
4
5
|
import { BUILTIN_PROVIDERS, HOSTED_BASE_URLS } from "../modules/providers/presets";
|
|
5
6
|
import { box } from "../ui/box";
|
|
6
7
|
import { getDefaultChannel } from "../output";
|
|
8
|
+
import { SETUP_DEFAULTS, SETUP_LANGUAGES, } from "../modules/setup/answers";
|
|
9
|
+
import { fetchProviderModels, scanLocalServers, testProviderChat, } from "../modules/setup/discovery";
|
|
7
10
|
import { SetupPrompt } from "./setup-prompt";
|
|
8
11
|
async function withSpinner(message, fn) {
|
|
9
12
|
const spinner = new Spinner({ channel: getDefaultChannel() });
|
|
@@ -15,12 +18,13 @@ async function withSpinner(message, fn) {
|
|
|
15
18
|
spinner.stop();
|
|
16
19
|
}
|
|
17
20
|
}
|
|
18
|
-
/**
|
|
21
|
+
/** Рендерит нумерованный список в рамке. */
|
|
19
22
|
function menuList(items) {
|
|
20
23
|
const lines = items.map((it, i) => ` ${pc.cyan(`[${i + 1}]`)} ${it.label}${it.url ? ` ${pc.dim(it.url)}` : ""}`);
|
|
21
24
|
for (const l of box(lines, { width: 72 }))
|
|
22
25
|
getDefaultChannel().writeLine(l);
|
|
23
26
|
}
|
|
27
|
+
/** Спрашивает строку; пустой ответ — defaultValue. */
|
|
24
28
|
function ask(rl, question, defaultValue) {
|
|
25
29
|
return new Promise((resolve) => {
|
|
26
30
|
const prompt = defaultValue ? `${question} [${defaultValue}]: ` : `${question}: `;
|
|
@@ -29,7 +33,7 @@ function ask(rl, question, defaultValue) {
|
|
|
29
33
|
});
|
|
30
34
|
});
|
|
31
35
|
}
|
|
32
|
-
/**
|
|
36
|
+
/** Парсит положительный int-ответ, иначе — дефолт промпта. */
|
|
33
37
|
function askInt(rl, question, defaultValue) {
|
|
34
38
|
return ask(rl, question, String(defaultValue)).then((raw) => {
|
|
35
39
|
const n = parseInt(raw, 10);
|
|
@@ -40,113 +44,28 @@ const PROVIDER_TYPES = BUILTIN_PROVIDERS.map((spec) => ({
|
|
|
40
44
|
value: spec.type,
|
|
41
45
|
label: spec.label,
|
|
42
46
|
}));
|
|
43
|
-
/**
|
|
44
|
-
const LANGUAGES = [
|
|
45
|
-
{ value: "en", label: "English" },
|
|
46
|
-
{ value: "ru", label: "Русский" },
|
|
47
|
-
];
|
|
48
|
-
const KNOWN_PORTS = [
|
|
49
|
-
{ port: 1234, label: "LM Studio" },
|
|
50
|
-
{ port: 11434, label: "Ollama" },
|
|
51
|
-
{ port: 8080, label: "vLLM / custom" },
|
|
52
|
-
{ port: 4891, label: "GPT4All" },
|
|
53
|
-
];
|
|
54
|
-
async function scanPorts() {
|
|
55
|
-
const found = [];
|
|
56
|
-
for (const { port, label } of KNOWN_PORTS) {
|
|
57
|
-
try {
|
|
58
|
-
const controller = new AbortController();
|
|
59
|
-
const timeout = setTimeout(() => controller.abort(), 2000);
|
|
60
|
-
const resp = await fetch(`http://localhost:${port}/v1/models`, {
|
|
61
|
-
signal: controller.signal,
|
|
62
|
-
});
|
|
63
|
-
clearTimeout(timeout);
|
|
64
|
-
if (resp.ok) {
|
|
65
|
-
found.push({ label, url: `http://localhost:${port}/v1` });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
/* port not available */
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return found;
|
|
73
|
-
}
|
|
74
|
-
async function fetchModels(apiBase, apiKey) {
|
|
75
|
-
try {
|
|
76
|
-
const url = `${apiBase.replace(/\/+$/, "")}/models`;
|
|
77
|
-
const controller = new AbortController();
|
|
78
|
-
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
79
|
-
const resp = await fetch(url, {
|
|
80
|
-
headers: {
|
|
81
|
-
Authorization: `Bearer ${apiKey}`,
|
|
82
|
-
"Content-Type": "application/json",
|
|
83
|
-
},
|
|
84
|
-
signal: controller.signal,
|
|
85
|
-
});
|
|
86
|
-
clearTimeout(timeout);
|
|
87
|
-
if (!resp.ok)
|
|
88
|
-
return [];
|
|
89
|
-
const data = (await resp.json());
|
|
90
|
-
const models = (data.data || data || [])
|
|
91
|
-
.map((m) => m.id || m.name || m.model || "")
|
|
92
|
-
.filter(Boolean);
|
|
93
|
-
return models;
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
return [];
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
async function testChat(apiBase, apiKey, model) {
|
|
100
|
-
try {
|
|
101
|
-
const url = `${apiBase.replace(/\/+$/, "")}/chat/completions`;
|
|
102
|
-
const controller = new AbortController();
|
|
103
|
-
const timeout = setTimeout(() => controller.abort(), 10000);
|
|
104
|
-
const resp = await fetch(url, {
|
|
105
|
-
method: "POST",
|
|
106
|
-
headers: {
|
|
107
|
-
Authorization: `Bearer ${apiKey}`,
|
|
108
|
-
"Content-Type": "application/json",
|
|
109
|
-
},
|
|
110
|
-
body: JSON.stringify({
|
|
111
|
-
model,
|
|
112
|
-
messages: [{ role: "user", content: 'Say "ok"' }],
|
|
113
|
-
max_tokens: 20,
|
|
114
|
-
}),
|
|
115
|
-
signal: controller.signal,
|
|
116
|
-
});
|
|
117
|
-
clearTimeout(timeout);
|
|
118
|
-
if (!resp.ok)
|
|
119
|
-
return false;
|
|
120
|
-
const data = (await resp.json());
|
|
121
|
-
const msg = data.choices?.[0]?.message;
|
|
122
|
-
const text = msg?.content || msg?.reasoning_content || "";
|
|
123
|
-
return text.length > 0;
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return false;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
47
|
+
/** Прогоняет визард первого запуска (язык → провайдер → … → review). Возвращает ответы. */
|
|
129
48
|
export async function runSetup(externalRl) {
|
|
130
49
|
getDefaultChannel().writeLine(t("setup.title"));
|
|
131
50
|
const rl = externalRl ?? new SetupPrompt();
|
|
132
51
|
const ownRl = !externalRl;
|
|
133
52
|
getDefaultChannel().writeLine(t("setup.language"));
|
|
134
|
-
menuList(
|
|
135
|
-
const languageChoice = await ask(rl, t("setup.select_language_num", { max:
|
|
53
|
+
menuList([...SETUP_LANGUAGES]);
|
|
54
|
+
const languageChoice = await ask(rl, t("setup.select_language_num", { max: SETUP_LANGUAGES.length }), "1");
|
|
136
55
|
const typedLang = languageChoice.trim().toLowerCase();
|
|
137
|
-
const byCode =
|
|
56
|
+
const byCode = SETUP_LANGUAGES.find((l) => l.value === typedLang);
|
|
138
57
|
const locale = byCode
|
|
139
58
|
? byCode.value
|
|
140
|
-
:
|
|
59
|
+
: SETUP_LANGUAGES[Math.max(0, Math.min(SETUP_LANGUAGES.length - 1, (parseInt(languageChoice, 10) || 1) - 1))].value;
|
|
141
60
|
setLocale(locale);
|
|
142
61
|
getDefaultChannel().writeLine(t("setup.select_provider"));
|
|
143
62
|
menuList(PROVIDER_TYPES);
|
|
144
63
|
const providerChoice = await ask(rl, t("setup.select_provider_num", { max: PROVIDER_TYPES.length }), "1");
|
|
145
|
-
const providerIdx = Math.max(0, Math.min(PROVIDER_TYPES.length - 1, (parseInt(providerChoice) || 1) - 1));
|
|
64
|
+
const providerIdx = Math.max(0, Math.min(PROVIDER_TYPES.length - 1, (parseInt(providerChoice, 10) || 1) - 1));
|
|
146
65
|
const provider = PROVIDER_TYPES[providerIdx].value;
|
|
147
66
|
let apiBase = "";
|
|
148
|
-
if (provider === "openai-compat") {
|
|
149
|
-
const found = await withSpinner(t("setup.scanning_spinner"), async () =>
|
|
67
|
+
if (provider === "openai-compat" || provider === "lmstudio") {
|
|
68
|
+
const found = await withSpinner(t("setup.scanning_spinner"), async () => scanLocalServers());
|
|
150
69
|
if (found.length > 0) {
|
|
151
70
|
getDefaultChannel().writeLine(t("setup.found_servers", { count: found.length }));
|
|
152
71
|
menuList([
|
|
@@ -154,7 +73,7 @@ export async function runSetup(externalRl) {
|
|
|
154
73
|
{ label: t("setup.custom_url") },
|
|
155
74
|
]);
|
|
156
75
|
const choice = await ask(rl, t("setup.select_server", { max: found.length + 1 }), "1");
|
|
157
|
-
const idx = (parseInt(choice) || 1) - 1;
|
|
76
|
+
const idx = (parseInt(choice, 10) || 1) - 1;
|
|
158
77
|
if (idx >= found.length) {
|
|
159
78
|
apiBase = await ask(rl, t("setup.api_base"), "http://localhost:1234/v1");
|
|
160
79
|
}
|
|
@@ -170,21 +89,21 @@ export async function runSetup(externalRl) {
|
|
|
170
89
|
else {
|
|
171
90
|
apiBase = HOSTED_BASE_URLS[provider] ?? "";
|
|
172
91
|
}
|
|
173
|
-
const defaultKey = provider === "openai-compat" ? "not-needed" : "";
|
|
92
|
+
const defaultKey = provider === "openai-compat" || provider === "lmstudio" ? "not-needed" : "";
|
|
174
93
|
const apiKey = await ask(rl, t("setup.api_key"), defaultKey);
|
|
175
|
-
const models = await withSpinner(t("setup.fetching_spinner"), () =>
|
|
94
|
+
const models = await withSpinner(t("setup.fetching_spinner"), () => fetchProviderModels(apiBase, apiKey));
|
|
176
95
|
let model = "";
|
|
177
96
|
if (models.length > 0) {
|
|
178
97
|
getDefaultChannel().writeLine(t("setup.available_models"));
|
|
179
98
|
menuList(models.slice(0, 10).map((m) => ({ label: m })));
|
|
180
99
|
const choice = await ask(rl, t("setup.select_model", { max: Math.min(models.length, 10) }), "1");
|
|
181
|
-
const idx = Math.max(0, Math.min(models.length - 1, (parseInt(choice) || 1) - 1));
|
|
100
|
+
const idx = Math.max(0, Math.min(models.length - 1, (parseInt(choice, 10) || 1) - 1));
|
|
182
101
|
model = models[idx];
|
|
183
102
|
}
|
|
184
103
|
else {
|
|
185
104
|
model = await ask(rl, t("setup.model_name"), "qwen/qwen3.5-9b");
|
|
186
105
|
}
|
|
187
|
-
const connected = await withSpinner(t("setup.testing_spinner", { model }), () =>
|
|
106
|
+
const connected = await withSpinner(t("setup.testing_spinner", { model }), () => testProviderChat(apiBase, apiKey, model));
|
|
188
107
|
if (connected) {
|
|
189
108
|
getDefaultChannel().writeLine(pc.green(t("setup.ok")));
|
|
190
109
|
}
|
|
@@ -192,24 +111,25 @@ export async function runSetup(externalRl) {
|
|
|
192
111
|
getDefaultChannel().writeLine(pc.yellow(t("setup.warning")));
|
|
193
112
|
}
|
|
194
113
|
getDefaultChannel().writeLine(t("setup.agent_settings"));
|
|
195
|
-
const contextWindow = await askInt(rl, t("setup.context_window"),
|
|
196
|
-
const maxIterations = await askInt(rl, t("setup.max_iters"),
|
|
114
|
+
const contextWindow = await askInt(rl, t("setup.context_window"), SETUP_DEFAULTS.contextWindow);
|
|
115
|
+
const maxIterations = await askInt(rl, t("setup.max_iters"), SETUP_DEFAULTS.maxToolIterations);
|
|
197
116
|
// Security settings
|
|
198
117
|
getDefaultChannel().writeLine(t("setup.security_header"));
|
|
199
118
|
getDefaultChannel().writeLine(pc.dim(t("setup.security_status_on")));
|
|
200
119
|
const configureSecurity = await ask(rl, t("setup.security_configure"), "n");
|
|
201
120
|
let securityBashBlock = false;
|
|
202
121
|
let securityFlagsBlock = false;
|
|
203
|
-
let
|
|
122
|
+
let securityPathsRestrict = false;
|
|
204
123
|
if (configureSecurity.toLowerCase() === "y" || configureSecurity.toLowerCase() === "yes") {
|
|
205
124
|
securityBashBlock = (await ask(rl, t("setup.security_bash_block"), "n")).toLowerCase() === "y";
|
|
206
125
|
securityFlagsBlock =
|
|
207
126
|
(await ask(rl, t("setup.security_flags_block"), "n")).toLowerCase() === "y";
|
|
208
|
-
|
|
127
|
+
const pathsAnswer = (await ask(rl, t("setup.security_paths_restrict"), "Y")).toLowerCase();
|
|
128
|
+
securityPathsRestrict = pathsAnswer !== "n" && pathsAnswer !== "no";
|
|
209
129
|
}
|
|
210
130
|
if (ownRl) {
|
|
211
|
-
//
|
|
212
|
-
// LineEditor
|
|
131
|
+
// Деттачим ридер промпта и ставим stdin на паузу, чтобы raw-режимный
|
|
132
|
+
// LineEditor REPL захватил его чисто (без leftover readline/raw-состояния).
|
|
213
133
|
rl.close();
|
|
214
134
|
}
|
|
215
135
|
const answers = {
|
|
@@ -222,7 +142,7 @@ export async function runSetup(externalRl) {
|
|
|
222
142
|
locale,
|
|
223
143
|
securityBashBlock,
|
|
224
144
|
securityFlagsBlock,
|
|
225
|
-
|
|
145
|
+
securityPathsRestrict,
|
|
226
146
|
};
|
|
227
147
|
getDefaultChannel().writeLine(pc.green(pc.bold(t("setup.complete"))));
|
|
228
148
|
const summary = [
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Точка входа `mma web`: bootstrap, старт браузерного UI, жизнь процесса до
|
|
3
|
+
* SIGINT/SIGTERM, затем чистое завершение.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync } from "node:fs";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { bootstrap } from "../core/bootstrap";
|
|
8
|
+
import { agentsMdPaths } from "../core/bootstrap";
|
|
9
|
+
import { getDefaultChannel } from "../output";
|
|
10
|
+
import { t } from "../i18n/index";
|
|
11
|
+
import { pc } from "../ui/colors";
|
|
12
|
+
import { startWebUi } from "../modules/webui/module";
|
|
13
|
+
import { tryOpenBrowser } from "../modules/webui/server";
|
|
14
|
+
import { globalMmaDir } from "../config/paths";
|
|
15
|
+
import { MMA_ENV } from "../config/env";
|
|
16
|
+
/**
|
|
17
|
+
* Парсит и валидирует флаг `--port`. `undefined`/пусто → `undefined` (рандомный
|
|
18
|
+
* порт). Нечисловые и вне диапазона бросают throw, а не молча откатываются на
|
|
19
|
+
* рандом: `--port abc` не должен маскироваться под рабочий bind.
|
|
20
|
+
*/
|
|
21
|
+
export function parsePort(raw) {
|
|
22
|
+
if (raw === undefined || raw.trim() === "")
|
|
23
|
+
return undefined;
|
|
24
|
+
const n = Number(raw);
|
|
25
|
+
if (!Number.isInteger(n) || n < 1 || n > 65535) {
|
|
26
|
+
throw new Error(t("cli.web_invalid_port", { port: raw }));
|
|
27
|
+
}
|
|
28
|
+
return n;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Аргументы bootstrap для `runWeb`. Вынесены, чтобы форвардинг CLI-флагов
|
|
32
|
+
* (особенно `--no-agents-md`) был юнит-тестируем без подъёма настоящего агента:
|
|
33
|
+
* Bun-моки модулей процесс-глобальны, спай на `bootstrap` потёк бы.
|
|
34
|
+
*/
|
|
35
|
+
export function webBootstrapArgs(opts) {
|
|
36
|
+
return [undefined, opts.projectDir, opts.noAgentsMd, true];
|
|
37
|
+
}
|
|
38
|
+
/** Запуск веб-режима: bootstrap → старт UI → ожидание SIGINT/SIGTERM. */
|
|
39
|
+
export async function runWeb(opts) {
|
|
40
|
+
// Транспорт читает их на старте (server/state): у порта нет поля конфига,
|
|
41
|
+
// так что CLI-флаг маппится на env, которую сервер уже уважает.
|
|
42
|
+
if (opts.port !== undefined)
|
|
43
|
+
process.env[MMA_ENV.WEBUI_PORT] = String(opts.port);
|
|
44
|
+
if (opts.host)
|
|
45
|
+
process.env[MMA_ENV.WEBUI_HOST] = opts.host;
|
|
46
|
+
if (opts.token)
|
|
47
|
+
process.env[MMA_ENV.WEBUI_TOKEN] = opts.token;
|
|
48
|
+
if (opts.noOpen)
|
|
49
|
+
process.env[MMA_ENV.WEBUI_OPEN] = "0";
|
|
50
|
+
const mmaDir = globalMmaDir();
|
|
51
|
+
const { hasDomainFiles } = await import("../config/domains");
|
|
52
|
+
const virgin = !existsSync(join(mmaDir, "config.json")) && !hasDomainFiles(mmaDir);
|
|
53
|
+
// exitOnComplete=true: в веб-режиме интерактивный промпт stdin не владеет.
|
|
54
|
+
const { agent, config, sessionManager, execModule, configDir, baseDir, noAgentsMd, indexerModule, memoryStore, skillsModule, pluginManager, toolRegistry, toolCtx, contextProbe, } = await bootstrap(...webBootstrapArgs(opts));
|
|
55
|
+
if (virgin) {
|
|
56
|
+
getDefaultChannel().writeLine(pc.yellow(` ${t("cli.web_setup_required")}`), "stderr");
|
|
57
|
+
}
|
|
58
|
+
const web = await startWebUi({
|
|
59
|
+
agent,
|
|
60
|
+
config,
|
|
61
|
+
sessionManager,
|
|
62
|
+
execModule,
|
|
63
|
+
memory: memoryStore,
|
|
64
|
+
skills: skillsModule,
|
|
65
|
+
plugins: pluginManager,
|
|
66
|
+
toolRegistry,
|
|
67
|
+
toolCtx,
|
|
68
|
+
configDir,
|
|
69
|
+
baseDir,
|
|
70
|
+
agentsMd: {
|
|
71
|
+
disabled: noAgentsMd,
|
|
72
|
+
files: noAgentsMd ? [] : agentsMdPaths(baseDir, configDir).filter((p) => existsSync(p)),
|
|
73
|
+
},
|
|
74
|
+
indexer: indexerModule,
|
|
75
|
+
contextProbe,
|
|
76
|
+
host: opts.host,
|
|
77
|
+
token: opts.token,
|
|
78
|
+
setupRequired: virgin,
|
|
79
|
+
});
|
|
80
|
+
const url = await web.url;
|
|
81
|
+
// Всегда показываем резолвленный workspace: shell-нарезка `-d` пути (пробелы,
|
|
82
|
+
// хвостовой бэкслэш) может молча указать не туда, и эта строка — единственное
|
|
83
|
+
// место, где веб-режим его репортит (зеркало баннера REPL).
|
|
84
|
+
getDefaultChannel().writeLine(t("cli.web_started", { url, dir: baseDir }));
|
|
85
|
+
if (!opts.noOpen)
|
|
86
|
+
tryOpenBrowser(url);
|
|
87
|
+
await new Promise((resolve) => {
|
|
88
|
+
const shutdown = () => {
|
|
89
|
+
process.off("SIGINT", shutdown);
|
|
90
|
+
process.off("SIGTERM", shutdown);
|
|
91
|
+
web.stop();
|
|
92
|
+
agent.shutdown();
|
|
93
|
+
resolve();
|
|
94
|
+
};
|
|
95
|
+
process.on("SIGINT", shutdown);
|
|
96
|
+
process.on("SIGTERM", shutdown);
|
|
97
|
+
});
|
|
98
|
+
}
|
package/dist/config/budget.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { t } from "../i18n/index";
|
|
2
2
|
const MIN_SHARE = 0.05;
|
|
3
3
|
const MAX_SHARE = 0.9;
|
|
4
|
-
/** system + reserve
|
|
4
|
+
/** system + reserve обязаны оставить место под историю. */
|
|
5
5
|
const MAX_COMBINED = 0.95;
|
|
6
|
-
/**
|
|
6
|
+
/** Режет окно контекста на части system / reserve / history. */
|
|
7
7
|
export function budgetBreakdown(config) {
|
|
8
8
|
const window = config.contextWindow;
|
|
9
9
|
const budget = config.contextBudget;
|
|
@@ -21,8 +21,8 @@ export function budgetBreakdown(config) {
|
|
|
21
21
|
};
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
* (`"range"`
|
|
24
|
+
* Проверяет и применяет смену доли. При неуспехе возвращает причину
|
|
25
|
+
* (`"range"` или `"combined"`), при успехе — null. Причину локализует вызывающий.
|
|
26
26
|
*/
|
|
27
27
|
export function setBudgetShare(config, key, value) {
|
|
28
28
|
if (!Number.isFinite(value) || value < MIN_SHARE || value > MAX_SHARE)
|
|
@@ -35,7 +35,7 @@ export function setBudgetShare(config, key, value) {
|
|
|
35
35
|
budget[key === "system" ? "systemPrompt" : "responseReserve"] = value;
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
|
-
/**
|
|
38
|
+
/** Локализованные строки разбивки бюджета (общие для `mma context` и `/context`). */
|
|
39
39
|
export function budgetBreakdownLines(config) {
|
|
40
40
|
const bd = budgetBreakdown(config);
|
|
41
41
|
const pct = (f) => Math.round(f * 100);
|