muonroi-cli 1.8.5 → 1.9.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/packages/agent-harness-core/src/event-filter.js +11 -0
- package/dist/packages/agent-harness-core/src/event-redact.js +7 -0
- package/dist/packages/agent-harness-core/src/event-tee.d.ts +20 -4
- package/dist/packages/agent-harness-core/src/event-tee.js +32 -5
- package/dist/packages/agent-harness-core/src/mcp-server.d.ts +14 -0
- package/dist/packages/agent-harness-core/src/mcp-server.js +86 -10
- package/dist/packages/agent-harness-core/src/predicate.d.ts +1 -1
- package/dist/packages/agent-harness-core/src/protocol.d.ts +24 -2
- package/dist/src/agent-harness/mock-model.d.ts +10 -0
- package/dist/src/agent-harness/mock-model.js +6 -2
- package/dist/src/chat/chat-keychain.d.ts +7 -12
- package/dist/src/chat/chat-keychain.js +19 -86
- package/dist/src/cli/keys.d.ts +8 -45
- package/dist/src/cli/keys.js +18 -324
- package/dist/src/council/clarifier.d.ts +4 -2
- package/dist/src/council/clarifier.js +158 -36
- package/dist/src/council/debate-planner.js +3 -2
- package/dist/src/council/debate.js +59 -5
- package/dist/src/council/index.d.ts +23 -0
- package/dist/src/council/index.js +37 -2
- package/dist/src/council/llm.d.ts +62 -0
- package/dist/src/council/llm.js +123 -23
- package/dist/src/council/panel-select.js +13 -3
- package/dist/src/council/planner.js +17 -0
- package/dist/src/council/preflight.d.ts +10 -0
- package/dist/src/council/preflight.js +36 -0
- package/dist/src/council/prompts.d.ts +9 -1
- package/dist/src/council/prompts.js +23 -6
- package/dist/src/council/types.d.ts +10 -0
- package/dist/src/ee/auth.d.ts +19 -0
- package/dist/src/ee/auth.js +39 -0
- package/dist/src/ee/client.js +28 -3
- package/dist/src/ee/ee-onboarding.js +6 -26
- package/dist/src/flow/compaction/compress.d.ts +2 -2
- package/dist/src/flow/compaction/compress.js +21 -8
- package/dist/src/flow/compaction/extract.d.ts +3 -3
- package/dist/src/flow/compaction/extract.js +6 -6
- package/dist/src/flow/compaction/index.d.ts +2 -1
- package/dist/src/flow/compaction/index.js +29 -3
- package/dist/src/flow/compaction/progress.d.ts +35 -0
- package/dist/src/flow/compaction/progress.js +35 -0
- package/dist/src/generated/version.d.ts +1 -1
- package/dist/src/generated/version.js +1 -1
- package/dist/src/gsd/flags.d.ts +11 -0
- package/dist/src/gsd/flags.js +19 -0
- package/dist/src/gsd/plan-council.js +104 -72
- package/dist/src/gsd/verdict-schema.d.ts +1 -1
- package/dist/src/headless/council-answers.js +4 -0
- package/dist/src/index.js +129 -260
- package/dist/src/lsp/builtins.js +3 -1
- package/dist/src/lsp/manager.d.ts +5 -1
- package/dist/src/lsp/manager.js +249 -3
- package/dist/src/lsp/npm-cache.d.ts +11 -1
- package/dist/src/lsp/npm-cache.js +17 -1
- package/dist/src/lsp/runtime.d.ts +6 -1
- package/dist/src/lsp/runtime.js +17 -1
- package/dist/src/lsp/types.d.ts +83 -1
- package/dist/src/lsp/types.js +10 -0
- package/dist/src/mcp/client-pool.js +43 -15
- package/dist/src/mcp/lsp-tools.d.ts +5 -1
- package/dist/src/mcp/lsp-tools.js +93 -2
- package/dist/src/mcp/mcp-keychain.d.ts +3 -5
- package/dist/src/mcp/mcp-keychain.js +9 -49
- package/dist/src/mcp/setup-guide-text.d.ts +1 -1
- package/dist/src/mcp/setup-guide-text.js +22 -2
- package/dist/src/mcp/tools-server.d.ts +10 -0
- package/dist/src/mcp/tools-server.js +10 -2
- package/dist/src/models/catalog.json +19 -19
- package/dist/src/orchestrator/ask-user.d.ts +61 -0
- package/dist/src/orchestrator/ask-user.js +65 -0
- package/dist/src/orchestrator/compaction.d.ts +2 -3
- package/dist/src/orchestrator/compaction.js +8 -8
- package/dist/src/orchestrator/council-manager.js +9 -8
- package/dist/src/orchestrator/council-request.d.ts +49 -0
- package/dist/src/orchestrator/council-request.js +62 -0
- package/dist/src/orchestrator/interactive-pause.d.ts +26 -0
- package/dist/src/orchestrator/interactive-pause.js +36 -0
- package/dist/src/orchestrator/message-processor.d.ts +4 -0
- package/dist/src/orchestrator/message-processor.js +26 -8
- package/dist/src/orchestrator/orchestrator.d.ts +25 -0
- package/dist/src/orchestrator/orchestrator.js +204 -50
- package/dist/src/orchestrator/preprocessor.js +2 -2
- package/dist/src/orchestrator/safety-askcard.d.ts +1 -1
- package/dist/src/orchestrator/safety-askcard.js +5 -2
- package/dist/src/orchestrator/safety-intercept.d.ts +5 -0
- package/dist/src/orchestrator/safety-intercept.js +7 -0
- package/dist/src/orchestrator/stall-watchdog.d.ts +8 -1
- package/dist/src/orchestrator/stall-watchdog.js +24 -3
- package/dist/src/orchestrator/stream-runner.d.ts +13 -3
- package/dist/src/orchestrator/stream-runner.js +54 -21
- package/dist/src/orchestrator/tool-engine.d.ts +19 -0
- package/dist/src/orchestrator/tool-engine.js +241 -25
- package/dist/src/orchestrator/turn-watchdog.d.ts +7 -0
- package/dist/src/orchestrator/turn-watchdog.js +38 -9
- package/dist/src/pil/agent-operating-contract.d.ts +1 -1
- package/dist/src/pil/agent-operating-contract.js +6 -4
- package/dist/src/pil/discovery.d.ts +1 -1
- package/dist/src/pil/discovery.js +2 -2
- package/dist/src/pil/layer1_5-complexity-size.d.ts +7 -0
- package/dist/src/pil/layer1_5-complexity-size.js +31 -5
- package/dist/src/pil/llm-classify.d.ts +78 -3
- package/dist/src/pil/llm-classify.js +351 -111
- package/dist/src/pil/native-capabilities-workbook.d.ts +1 -1
- package/dist/src/pil/native-capabilities-workbook.js +7 -0
- package/dist/src/pil/pipeline.js +2 -0
- package/dist/src/pil/repo-grounding-probe.d.ts +15 -0
- package/dist/src/pil/repo-grounding-probe.js +136 -0
- package/dist/src/pil/repo-structure-hints.d.ts +7 -0
- package/dist/src/pil/repo-structure-hints.js +45 -0
- package/dist/src/product-loop/artifact-io.js +4 -0
- package/dist/src/product-loop/criteria-seed.d.ts +51 -0
- package/dist/src/product-loop/criteria-seed.js +200 -0
- package/dist/src/product-loop/discovery-interview.d.ts +9 -0
- package/dist/src/product-loop/discovery-interview.js +37 -18
- package/dist/src/product-loop/discovery-recommender.js +2 -1
- package/dist/src/product-loop/discovery-schema.js +14 -1
- package/dist/src/product-loop/discovery-triage.d.ts +23 -0
- package/dist/src/product-loop/discovery-triage.js +109 -0
- package/dist/src/product-loop/gather.js +150 -2
- package/dist/src/product-loop/index.js +7 -0
- package/dist/src/product-loop/loop-driver.js +21 -8
- package/dist/src/product-loop/phase-plan.d.ts +16 -0
- package/dist/src/product-loop/phase-plan.js +42 -4
- package/dist/src/product-loop/phase-rituals.d.ts +3 -0
- package/dist/src/product-loop/phase-rituals.js +8 -3
- package/dist/src/product-loop/phase-runner.js +30 -11
- package/dist/src/product-loop/plan-adherence-review.d.ts +26 -0
- package/dist/src/product-loop/plan-adherence-review.js +144 -0
- package/dist/src/product-loop/sprint-runner.d.ts +62 -0
- package/dist/src/product-loop/sprint-runner.js +309 -8
- package/dist/src/product-loop/types.d.ts +25 -0
- package/dist/src/providers/anthropic.d.ts +9 -8
- package/dist/src/providers/anthropic.js +13 -47
- package/dist/src/providers/auth/grok-oauth.d.ts +1 -0
- package/dist/src/providers/auth/grok-oauth.js +30 -5
- package/dist/src/providers/auth/openai-oauth.d.ts +1 -0
- package/dist/src/providers/auth/openai-oauth.js +14 -0
- package/dist/src/providers/auth/token-store.d.ts +9 -9
- package/dist/src/providers/auth/token-store.js +8 -70
- package/dist/src/providers/auth/types.d.ts +8 -0
- package/dist/src/providers/env-store.d.ts +17 -0
- package/dist/src/providers/env-store.js +228 -0
- package/dist/src/providers/keychain.d.ts +21 -17
- package/dist/src/providers/keychain.js +124 -135
- package/dist/src/providers/runtime.d.ts +24 -9
- package/dist/src/providers/runtime.js +48 -37
- package/dist/src/providers/strategies/thinking-mode.js +9 -1
- package/dist/src/providers/strategies/xai.strategy.js +27 -0
- package/dist/src/providers/warm.d.ts +65 -0
- package/dist/src/providers/warm.js +145 -0
- package/dist/src/self-qa/agentic-loop.js +3 -2
- package/dist/src/storage/transcript.js +56 -2
- package/dist/src/tools/git-safety.d.ts +19 -0
- package/dist/src/tools/git-safety.js +168 -0
- package/dist/src/tools/native-tools.d.ts +1 -1
- package/dist/src/tools/native-tools.js +76 -1
- package/dist/src/tools/registry.d.ts +17 -0
- package/dist/src/tools/registry.js +116 -1
- package/dist/src/types/index.d.ts +29 -1
- package/dist/src/ui/app.js +67 -4
- package/dist/src/ui/components/agent-rail-activities.d.ts +26 -0
- package/dist/src/ui/components/agent-rail-activities.js +47 -0
- package/dist/src/ui/components/compact-progress-card.d.ts +24 -0
- package/dist/src/ui/components/compact-progress-card.js +42 -0
- package/dist/src/ui/components/council-phase-timeline.js +17 -2
- package/dist/src/ui/components/council-question-card.js +1 -0
- package/dist/src/ui/components/message-view.d.ts +15 -0
- package/dist/src/ui/components/message-view.js +50 -1
- package/dist/src/ui/components/tool-group.d.ts +15 -3
- package/dist/src/ui/components/tool-group.js +69 -11
- package/dist/src/ui/containers/modals-layer.d.ts +2 -1
- package/dist/src/ui/containers/modals-layer.js +2 -2
- package/dist/src/ui/council-harness-event.d.ts +57 -0
- package/dist/src/ui/council-harness-event.js +46 -0
- package/dist/src/ui/heartbeat-debug.d.ts +29 -0
- package/dist/src/ui/heartbeat-debug.js +45 -0
- package/dist/src/ui/modals/api-key-modal.js +1 -1
- package/dist/src/ui/modals/model-picker-modal.d.ts +8 -18
- package/dist/src/ui/modals/model-picker-modal.js +8 -10
- package/dist/src/ui/slash/ee.js +81 -0
- package/dist/src/ui/slash/menu-items.js +11 -2
- package/dist/src/ui/use-app-logic.js +354 -224
- package/dist/src/ui/utils/agent-activities.d.ts +39 -0
- package/dist/src/ui/utils/agent-activities.js +96 -0
- package/dist/src/ui/utils/group-tool-entries.d.ts +26 -0
- package/dist/src/ui/utils/group-tool-entries.js +111 -0
- package/dist/src/ui/utils/tool-summary.d.ts +21 -0
- package/dist/src/ui/utils/tool-summary.js +91 -0
- package/dist/src/utils/event-loop-monitor.d.ts +85 -0
- package/dist/src/utils/event-loop-monitor.js +107 -0
- package/dist/src/utils/llm-deadline.d.ts +14 -0
- package/dist/src/utils/llm-deadline.js +19 -0
- package/dist/src/utils/loop-profiler.d.ts +102 -0
- package/dist/src/utils/loop-profiler.js +202 -0
- package/dist/src/utils/settings.d.ts +27 -0
- package/dist/src/utils/settings.js +38 -2
- package/dist/src/utils/side-question.d.ts +1 -2
- package/dist/src/utils/side-question.js +2 -2
- package/dist/src/verify/entrypoint.js +51 -16
- package/dist/src/verify/orchestrator.d.ts +1 -1
- package/dist/src/verify/orchestrator.js +20 -3
- package/package.json +1 -2
- package/dist/src/cli/bw-vault.d.ts +0 -55
- package/dist/src/cli/bw-vault.js +0 -133
- package/dist/src/mcp/ee-tools.d.ts +0 -46
- package/dist/src/mcp/ee-tools.js +0 -194
package/dist/src/index.js
CHANGED
|
@@ -24,8 +24,7 @@ import { createAbortContext } from "./orchestrator/abort.js";
|
|
|
24
24
|
import { completeDelegation, failDelegation, loadDelegation } from "./orchestrator/delegations.js";
|
|
25
25
|
import { Agent } from "./orchestrator/orchestrator.js";
|
|
26
26
|
import { createPendingCallsLog } from "./orchestrator/pending-calls.js";
|
|
27
|
-
import {
|
|
28
|
-
import { listStoredProviders, loadKeyForProvider, setKeyForProvider } from "./providers/keychain.js";
|
|
27
|
+
import { loadKeyForProvider, setKeyForProvider } from "./providers/keychain.js";
|
|
29
28
|
import { detectProviderForModel } from "./providers/runtime.js";
|
|
30
29
|
import { loadConfig } from "./storage/config.js";
|
|
31
30
|
import { loadUsage } from "./storage/usage-cap.js";
|
|
@@ -35,7 +34,7 @@ import { runScriptManagedUninstall } from "./utils/install-manager.js";
|
|
|
35
34
|
import { getApiKey, getBaseURL, getCurrentModel, saveUserSettings } from "./utils/settings.js";
|
|
36
35
|
import { runUpdate } from "./utils/update-checker.js";
|
|
37
36
|
import { buildVerifyPrompt, getVerifyCliError } from "./verify/entrypoint.js";
|
|
38
|
-
// Hydrate chat secrets
|
|
37
|
+
// Hydrate chat secrets (no-op now: secrets live in the environment/env-store)
|
|
39
38
|
await hydrateChatEnvFromKeychain();
|
|
40
39
|
const exitCleanlyOnSigterm = () => {
|
|
41
40
|
process.exit(0);
|
|
@@ -61,6 +60,78 @@ export function appendCrashLog(label, msg) {
|
|
|
61
60
|
/* crash.log is best-effort diagnostics; the logger itself must never throw */
|
|
62
61
|
}
|
|
63
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Arm the event-loop freeze diagnostics for this TUI session.
|
|
65
|
+
*
|
|
66
|
+
* Every existing hang guard in this codebase (provider stall watchdog, turn
|
|
67
|
+
* watchdog) is driven by a timer, so all of them are blind to a blocked event
|
|
68
|
+
* loop — a timer cannot fire while the JS thread is stuck. Measured live
|
|
69
|
+
* 2026-07-16 (session 90c3ff533826): the TUI froze 304.5s, input replayed on
|
|
70
|
+
* recovery, and the 120s stall watchdog only rescued the turn 6.7s AFTER the
|
|
71
|
+
* loop came back. This closes that blind spot: the monitor reports the block
|
|
72
|
+
* once the loop frees up, and — when armed — the CPU profiler supplies the
|
|
73
|
+
* stack captured DURING it.
|
|
74
|
+
*
|
|
75
|
+
* Best-effort: never let diagnostics break the CLI.
|
|
76
|
+
*/
|
|
77
|
+
async function startFreezeDiagnostics() {
|
|
78
|
+
try {
|
|
79
|
+
const { getLoopBlockThresholdMs, isLoopProfileEnabled } = await import("./utils/settings.js");
|
|
80
|
+
const { startEventLoopMonitor } = await import("./utils/event-loop-monitor.js");
|
|
81
|
+
const { logger } = await import("./utils/logger.js");
|
|
82
|
+
const thresholdMs = getLoopBlockThresholdMs();
|
|
83
|
+
if (thresholdMs <= 0)
|
|
84
|
+
return;
|
|
85
|
+
const profiler = isLoopProfileEnabled()
|
|
86
|
+
? await (await import("./utils/loop-profiler.js")).createLoopProfiler()
|
|
87
|
+
: null;
|
|
88
|
+
if (profiler) {
|
|
89
|
+
// Bound segment size. Loop-driven ON PURPOSE: it cannot rotate during a
|
|
90
|
+
// block, so the segment covering a freeze survives to be captured.
|
|
91
|
+
const rotateTimer = setInterval(() => profiler.rotate(), 30_000);
|
|
92
|
+
rotateTimer.unref?.();
|
|
93
|
+
}
|
|
94
|
+
logger.info("cli", `[freeze] diagnostics armed (threshold ${thresholdMs}ms)`, {
|
|
95
|
+
thresholdMs,
|
|
96
|
+
profiler: profiler ? "on" : "off",
|
|
97
|
+
});
|
|
98
|
+
startEventLoopMonitor({
|
|
99
|
+
thresholdMs,
|
|
100
|
+
onBlock: (block) => {
|
|
101
|
+
logger.warn("cli", `[freeze] event loop blocked for ${block.blockedMs}ms — UI was frozen and no timer could fire`, {
|
|
102
|
+
blockedMs: block.blockedMs,
|
|
103
|
+
breadcrumb: block.breadcrumb,
|
|
104
|
+
detectedAt: block.detectedAt,
|
|
105
|
+
profiler: profiler ? "armed" : "off (set MUONROI_LOOP_PROFILE=1 for the culprit stack)",
|
|
106
|
+
});
|
|
107
|
+
if (!profiler)
|
|
108
|
+
return;
|
|
109
|
+
void profiler
|
|
110
|
+
.capture(block.breadcrumb ?? "unknown")
|
|
111
|
+
.then((res) => {
|
|
112
|
+
if (res) {
|
|
113
|
+
// The leaf is usually a native primitive (`now`, a regex step) —
|
|
114
|
+
// true but useless as a headline. Lead with the first frame that
|
|
115
|
+
// has a file:line, i.e. OUR code.
|
|
116
|
+
const suspect = res.hotStack.find((f) => !f.endsWith("(native)")) ?? res.hotStack[0];
|
|
117
|
+
logger.warn("cli", `[freeze] blocked in: ${suspect ?? "(unattributed)"}`, {
|
|
118
|
+
hotStack: res.hotStack,
|
|
119
|
+
file: res.file,
|
|
120
|
+
blockedMs: block.blockedMs,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
.catch((err) => {
|
|
125
|
+
logger.error("cli", `[freeze] profile capture failed: ${err?.message}`, { error: err });
|
|
126
|
+
});
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch (err) {
|
|
131
|
+
// Diagnostics must never take the CLI down with them.
|
|
132
|
+
appendCrashLog("FREEZE_DIAG", err instanceof Error ? (err.stack ?? err.message) : String(err));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
64
135
|
process.on("uncaughtException", (err) => {
|
|
65
136
|
appendCrashLog("UNCAUGHT", err.stack || err.message);
|
|
66
137
|
console.error("Fatal:", err.message);
|
|
@@ -111,7 +182,7 @@ setRenderSink((lineOrChunk) => {
|
|
|
111
182
|
// Provider console URLs are sourced from providers/capabilities.ts via ProviderCapabilities.consoleSignupURL().
|
|
112
183
|
/**
|
|
113
184
|
* Try to find an API key for the model the CLI is about to run with.
|
|
114
|
-
* Resolution: env (
|
|
185
|
+
* Resolution: per-provider env var (env-store), else OAuth sentinel.
|
|
115
186
|
* Returns null if nothing usable is configured anywhere.
|
|
116
187
|
*/
|
|
117
188
|
async function resolveKeyForModel(modelId) {
|
|
@@ -154,187 +225,6 @@ async function hasOAuthForModel(modelId) {
|
|
|
154
225
|
return false;
|
|
155
226
|
}
|
|
156
227
|
}
|
|
157
|
-
/**
|
|
158
|
-
* First-run wizard. If the keychain already has keys, prints a hint
|
|
159
|
-
* (model probably doesn't match any stored provider). Otherwise prompts
|
|
160
|
-
* for provider + key and persists to the OS keychain.
|
|
161
|
-
*/
|
|
162
|
-
/**
|
|
163
|
-
* Supported splash providers — mirrors SPLASH_PROVIDERS in ui/app.tsx.
|
|
164
|
-
* The wizard only surfaces these; other providers still work programmatically.
|
|
165
|
-
*/
|
|
166
|
-
const WIZARD_PROVIDERS = ["deepseek", "zai", "opencode-go", "xai"];
|
|
167
|
-
async function firstRunWizard(currentModel) {
|
|
168
|
-
let rl;
|
|
169
|
-
try {
|
|
170
|
-
rl = createInterface({ input: process.stdin, output: process.stderr });
|
|
171
|
-
const ask = (q) => new Promise((resolve) => rl.question(q, (answer) => resolve(answer)));
|
|
172
|
-
process.stderr.write("\nWelcome to muonroi-cli!\n\n");
|
|
173
|
-
const stored = await listStoredProviders();
|
|
174
|
-
if (stored.length > 0) {
|
|
175
|
-
process.stderr.write(`Keys already in keychain for: ${stored.join(", ")}\n`);
|
|
176
|
-
if (currentModel) {
|
|
177
|
-
const provider = detectProviderForModel(currentModel);
|
|
178
|
-
process.stderr.write(`Current model '${currentModel}' uses provider '${provider}', which has no stored key.\n`);
|
|
179
|
-
}
|
|
180
|
-
process.stderr.write("\nOptions:\n" +
|
|
181
|
-
" 1. Run with a model that matches a stored provider:\n" +
|
|
182
|
-
" muonroi-cli --model <model-id>\n" +
|
|
183
|
-
" 2. Open /providers inside the TUI to add another key.\n" +
|
|
184
|
-
" 3. muonroi-cli keys set <provider>\n\n");
|
|
185
|
-
rl.close();
|
|
186
|
-
return null;
|
|
187
|
-
}
|
|
188
|
-
process.stderr.write("Pick how you want to add credentials:\n\n");
|
|
189
|
-
process.stderr.write(" 1. Paste an API key (most common)\n");
|
|
190
|
-
process.stderr.write(" 2. Import an encrypted bundle file (from another device)\n");
|
|
191
|
-
process.stderr.write(" 3. Sync from a Bitwarden vault\n");
|
|
192
|
-
process.stderr.write(" 4. Skip — set up later via /providers inside the TUI\n\n");
|
|
193
|
-
const actionChoice = (await ask("Choice [1-4, default 1]: ")).trim() || "1";
|
|
194
|
-
if (actionChoice === "4") {
|
|
195
|
-
process.stderr.write("\nSkipped. Open /providers inside the TUI to add a key any time.\n");
|
|
196
|
-
rl.close();
|
|
197
|
-
return null;
|
|
198
|
-
}
|
|
199
|
-
if (actionChoice === "2") {
|
|
200
|
-
const file = (await ask("Path to bundle file: ")).trim();
|
|
201
|
-
if (!file) {
|
|
202
|
-
process.stderr.write("No file provided. Aborted.\n");
|
|
203
|
-
rl.close();
|
|
204
|
-
return null;
|
|
205
|
-
}
|
|
206
|
-
const passphrase = await ask("Bundle passphrase: ");
|
|
207
|
-
try {
|
|
208
|
-
const { readFileSync: readFile } = await import("node:fs");
|
|
209
|
-
const { decryptBundle } = await import("./cli/keys-bundle.js");
|
|
210
|
-
const raw = readFile(file, "utf8");
|
|
211
|
-
const bundle = JSON.parse(raw);
|
|
212
|
-
const payload = decryptBundle(bundle, passphrase);
|
|
213
|
-
let imported = 0;
|
|
214
|
-
for (const [prov, key] of Object.entries(payload.providers)) {
|
|
215
|
-
if (!WIZARD_PROVIDERS.includes(prov))
|
|
216
|
-
continue;
|
|
217
|
-
if (typeof key !== "string" || key.length < 20)
|
|
218
|
-
continue;
|
|
219
|
-
const ok = await setKeyForProvider(prov, key);
|
|
220
|
-
if (ok) {
|
|
221
|
-
imported++;
|
|
222
|
-
process.stderr.write(` ✓ ${prov} → keychain\n`);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
process.stderr.write(`\nImported ${imported} key(s). Launch the TUI to start.\n`);
|
|
226
|
-
rl.close();
|
|
227
|
-
// Return any imported key just so caller treats setup as done.
|
|
228
|
-
return imported > 0 ? "imported" : null;
|
|
229
|
-
}
|
|
230
|
-
catch (err) {
|
|
231
|
-
process.stderr.write(`\nImport failed: ${err.message}\n`);
|
|
232
|
-
rl.close();
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
if (actionChoice === "3") {
|
|
237
|
-
const password = await ask("Bitwarden master password: ");
|
|
238
|
-
try {
|
|
239
|
-
const { unlockWithPassword, listSecureNotesByPrefix } = await import("./cli/bw-vault.js");
|
|
240
|
-
const unlock = await unlockWithPassword(password);
|
|
241
|
-
if (!unlock.ok || !unlock.session) {
|
|
242
|
-
process.stderr.write(`\nBitwarden unlock failed: ${unlock.error ?? "unknown error"}\n`);
|
|
243
|
-
rl.close();
|
|
244
|
-
return null;
|
|
245
|
-
}
|
|
246
|
-
const list = await listSecureNotesByPrefix(unlock.session, "muonroi-cli/");
|
|
247
|
-
if (!list.ok) {
|
|
248
|
-
process.stderr.write(`\nBitwarden list failed: ${list.error}\n`);
|
|
249
|
-
rl.close();
|
|
250
|
-
return null;
|
|
251
|
-
}
|
|
252
|
-
let imported = 0;
|
|
253
|
-
for (const item of list.items) {
|
|
254
|
-
const prov = item.name.slice("muonroi-cli/".length);
|
|
255
|
-
if (!WIZARD_PROVIDERS.includes(prov))
|
|
256
|
-
continue;
|
|
257
|
-
if (item.notes.length < 20)
|
|
258
|
-
continue;
|
|
259
|
-
const ok = await setKeyForProvider(prov, item.notes);
|
|
260
|
-
if (ok) {
|
|
261
|
-
imported++;
|
|
262
|
-
process.stderr.write(` ✓ ${prov} → keychain\n`);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
process.stderr.write(`\nImported ${imported} key(s) from Bitwarden. Launch the TUI to start.\n`);
|
|
266
|
-
rl.close();
|
|
267
|
-
return imported > 0 ? "imported" : null;
|
|
268
|
-
}
|
|
269
|
-
catch (err) {
|
|
270
|
-
process.stderr.write(`\nBitwarden sync failed: ${err.message}\n`);
|
|
271
|
-
rl.close();
|
|
272
|
-
return null;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
// Default path: paste an API key for one of WIZARD_PROVIDERS.
|
|
276
|
-
process.stderr.write("\nSupported providers:\n\n");
|
|
277
|
-
WIZARD_PROVIDERS.forEach((p, i) => {
|
|
278
|
-
process.stderr.write(` ${i + 1}. ${p.padEnd(12)} ${getProviderCapabilities(p).consoleSignupURL()}\n`);
|
|
279
|
-
});
|
|
280
|
-
process.stderr.write("\n");
|
|
281
|
-
const choice = (await ask(`Provider [1-${WIZARD_PROVIDERS.length}, default 1]: `)).trim();
|
|
282
|
-
const idx = choice ? Number.parseInt(choice, 10) - 1 : 0;
|
|
283
|
-
if (!Number.isFinite(idx) || idx < 0 || idx >= WIZARD_PROVIDERS.length) {
|
|
284
|
-
process.stderr.write("Invalid choice — aborted.\n");
|
|
285
|
-
rl.close();
|
|
286
|
-
return null;
|
|
287
|
-
}
|
|
288
|
-
const provider = WIZARD_PROVIDERS[idx];
|
|
289
|
-
if (!provider) {
|
|
290
|
-
process.stderr.write("Invalid choice — aborted.\n");
|
|
291
|
-
rl.close();
|
|
292
|
-
return null;
|
|
293
|
-
}
|
|
294
|
-
process.stderr.write(`\nGet a key here: ${getProviderCapabilities(provider).consoleSignupURL()}\n`);
|
|
295
|
-
const raw = await ask(`Paste your ${provider} API key: `);
|
|
296
|
-
const trimmed = raw.trim();
|
|
297
|
-
if (!trimmed) {
|
|
298
|
-
process.stderr.write("No key provided. Aborted.\n");
|
|
299
|
-
rl.close();
|
|
300
|
-
return null;
|
|
301
|
-
}
|
|
302
|
-
if (trimmed.length < 20) {
|
|
303
|
-
process.stderr.write("Key looks too short (< 20 chars). Aborted.\n");
|
|
304
|
-
rl.close();
|
|
305
|
-
return null;
|
|
306
|
-
}
|
|
307
|
-
try {
|
|
308
|
-
const ok = await setKeyForProvider(provider, trimmed);
|
|
309
|
-
if (ok) {
|
|
310
|
-
process.stderr.write(`\nStored ${provider} key in OS keychain.\n`);
|
|
311
|
-
process.stderr.write("Tip: run 'muonroi-cli keys export ~/keys.json' to back up + move to other devices.\n");
|
|
312
|
-
if (currentModel) {
|
|
313
|
-
const currentProvider = detectProviderForModel(currentModel);
|
|
314
|
-
if (currentProvider !== provider) {
|
|
315
|
-
process.stderr.write(`\nNote: defaultModel '${currentModel}' is on '${currentProvider}'. ` +
|
|
316
|
-
`Edit ~/.muonroi-cli/user-settings.json or rerun with --model to use ${provider}.\n`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
process.stderr.write("\nOS keychain unavailable (keytar or secret service backend).\n" +
|
|
322
|
-
"Linux: sudo dnf install libsecret (Fedora) or sudo apt-get install libsecret-1-0 (Ubuntu).\n" +
|
|
323
|
-
"Key will be used for this session only. For persistence across runs:\n" +
|
|
324
|
-
` export ${provider.toUpperCase()}_API_KEY=...\n`);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
catch (err) {
|
|
328
|
-
process.stderr.write(`\nWarning: failed to store key in keychain: ${err.message}\n`);
|
|
329
|
-
}
|
|
330
|
-
rl.close();
|
|
331
|
-
return trimmed;
|
|
332
|
-
}
|
|
333
|
-
catch {
|
|
334
|
-
rl?.close();
|
|
335
|
-
return null;
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
228
|
async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi, session, initialMessage, permissionMode = "safe", injectHalt = false, injectHaltSprint = false) {
|
|
339
229
|
// ── Plan 00-07 boot order ──────────────────────────────────────────────────
|
|
340
230
|
// 1. redactor.installGlobalPatches() — already at top of file (line 6).
|
|
@@ -401,6 +291,14 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
401
291
|
const { warmMcpClients } = await import("./mcp/client-pool.js");
|
|
402
292
|
const { loadMcpServers } = await import("./utils/settings.js");
|
|
403
293
|
await warmMcpClients(loadMcpServers(), !session);
|
|
294
|
+
// Register a factory for every credentialed provider, not just the session's.
|
|
295
|
+
// A model resolved for another provider (compaction, classify, a sub-agent,
|
|
296
|
+
// a mode-specific model) can then be routed to ITS OWN endpoint instead of
|
|
297
|
+
// being POSTed to the session provider's — the 404 in session 0c6728ba1a25
|
|
298
|
+
// (`gpt-5.4` → api.x.ai) existed only because the openai factory this
|
|
299
|
+
// redirect needs had never been built. Best-effort: never blocks boot.
|
|
300
|
+
const { warmProviderFactories } = await import("./providers/warm.js");
|
|
301
|
+
await warmProviderFactories();
|
|
404
302
|
const agent = new Agent(apiKey, baseURL, model, maxToolRounds, {
|
|
405
303
|
session,
|
|
406
304
|
batchApi,
|
|
@@ -445,6 +343,10 @@ async function startInteractive(apiKey, baseURL, model, maxToolRounds, batchApi,
|
|
|
445
343
|
// process.exit(1) (see the unhandledRejection handler above). Cleared in
|
|
446
344
|
// restoreTerminalSync() the moment we begin tearing the terminal back down.
|
|
447
345
|
setTuiActive(true);
|
|
346
|
+
// Armed only once the TUI owns the terminal: this hunts a freeze of the
|
|
347
|
+
// interactive session, and the logger only routes to debug.log (instead of
|
|
348
|
+
// stdout, which would corrupt the framebuffer) once the TUI is active.
|
|
349
|
+
await startFreezeDiagnostics();
|
|
448
350
|
// Deliberate debug-console toggle. With openConsoleOnError:false the overlay
|
|
449
351
|
// never auto-pops, so F12 is the ONLY way in — and, crucially, back out:
|
|
450
352
|
// console.toggle() hides it when it is visible+focused, so it can never trap.
|
|
@@ -761,7 +663,7 @@ async function runBackgroundDelegation(jobPath, options) {
|
|
|
761
663
|
const model = explicitModel ? normalizeModelId(explicitModel) : undefined;
|
|
762
664
|
// Resolve API key: explicit flag > legacy env/settings.apiKey > per-provider keychain
|
|
763
665
|
// (matches the foreground flow — delegations were previously broken when the user
|
|
764
|
-
// only had a per-provider key in the
|
|
666
|
+
// only had a per-provider key in the environment, e.g. deepseek.)
|
|
765
667
|
let apiKey = stringOption(options.apiKey) || getApiKey();
|
|
766
668
|
if (!apiKey) {
|
|
767
669
|
const modelForResolve = model ?? delegation.model ?? getCurrentModel("agent");
|
|
@@ -830,8 +732,8 @@ function resolveConfig(options) {
|
|
|
830
732
|
// flag or MUONROI_MAX_TOOL_ROUNDS env.
|
|
831
733
|
const maxToolRounds = parseInt(stringOption(options.maxToolRounds) || "100", 10) || 100;
|
|
832
734
|
if (typeof options.apiKey === "string" && process.env.MUONROI_TEST_NO_PERSIST !== "1") {
|
|
833
|
-
// Persist to
|
|
834
|
-
// Fire-and-forget:
|
|
735
|
+
// Persist to the env-store (per-provider) instead of plaintext settings.json.
|
|
736
|
+
// Fire-and-forget: env-store write is sync; the key is also set in process.env so it
|
|
835
737
|
// works for this run via `apiKey` above and the user can re-supply it next invocation.
|
|
836
738
|
void persistApiKeyToKeychain(options.apiKey, stringOption(options.model)).catch(() => { });
|
|
837
739
|
}
|
|
@@ -1015,41 +917,43 @@ program
|
|
|
1015
917
|
await loadCatalog().catch(() => {
|
|
1016
918
|
catalogLoadFailed = true;
|
|
1017
919
|
});
|
|
920
|
+
// Load the env-store (~/.muonroi-cli/.env) into process.env and migrate any
|
|
921
|
+
// legacy keychain/settings keys BEFORE any key resolution. Best-effort.
|
|
922
|
+
try {
|
|
923
|
+
const { loadEnvFileIntoProcess } = await import("./providers/env-store.js");
|
|
924
|
+
loadEnvFileIntoProcess();
|
|
925
|
+
const { migrateLegacyKeysToEnv } = await import("./providers/keychain.js");
|
|
926
|
+
await migrateLegacyKeysToEnv();
|
|
927
|
+
}
|
|
928
|
+
catch (err) {
|
|
929
|
+
console.error(`[muonroi-cli] env-store init failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
930
|
+
}
|
|
1018
931
|
if (options.backgroundTaskFile) {
|
|
1019
932
|
await runBackgroundDelegation(options.backgroundTaskFile, options);
|
|
1020
933
|
return;
|
|
1021
934
|
}
|
|
1022
935
|
const config = resolveConfig(options);
|
|
1023
|
-
//
|
|
1024
|
-
//
|
|
1025
|
-
|
|
936
|
+
// One auth mode per provider (OAuth XOR API key): if the resolved model's
|
|
937
|
+
// provider has an OAuth token, OAuth wins OUTRIGHT — a stored API key
|
|
938
|
+
// (env/flag/settings) is ignored, never blended. Only when there is no
|
|
939
|
+
// OAuth token do we fall back to the provider's env API key. The "oauth"
|
|
940
|
+
// sentinel satisfies downstream gating (which only checks for a truthy
|
|
941
|
+
// apiKey); the runtime injects Bearer headers.
|
|
942
|
+
{
|
|
1026
943
|
const modelForResolve = config.model ?? getCurrentModel("agent");
|
|
1027
|
-
|
|
1028
|
-
if (keychainKey) {
|
|
1029
|
-
config.apiKey = keychainKey;
|
|
1030
|
-
}
|
|
1031
|
-
else if (await hasOAuthForModel(modelForResolve)) {
|
|
1032
|
-
// OAuth-authenticated provider — runtime will inject Bearer headers.
|
|
1033
|
-
// Set placeholder so downstream gating code (which only checks for
|
|
1034
|
-
// a truthy apiKey) is satisfied.
|
|
944
|
+
if (await hasOAuthForModel(modelForResolve)) {
|
|
1035
945
|
config.apiKey = "oauth";
|
|
1036
946
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
const modelForWizard = config.model ?? getCurrentModel("agent");
|
|
1042
|
-
const wizardKey = await firstRunWizard(modelForWizard);
|
|
1043
|
-
if (wizardKey) {
|
|
1044
|
-
// Key is already persisted to the OS keychain by the wizard. We DO NOT
|
|
1045
|
-
// write it back to settings.json (avoids resurrecting plaintext that
|
|
1046
|
-
// 'keys cleanup-settings' just removed).
|
|
1047
|
-
config.apiKey = wizardKey;
|
|
1048
|
-
}
|
|
1049
|
-
else {
|
|
1050
|
-
process.exit(1);
|
|
947
|
+
else if (!config.apiKey) {
|
|
948
|
+
const envKey = await resolveKeyForModel(modelForResolve);
|
|
949
|
+
if (envKey)
|
|
950
|
+
config.apiKey = envKey;
|
|
1051
951
|
}
|
|
1052
952
|
}
|
|
953
|
+
// First run with no credentials does NOT block: boot straight into the TUI.
|
|
954
|
+
// The in-chat provider picker (/providers) handles auth on demand.
|
|
955
|
+
// Headless (`--prompt`/`--verify`) with no key still fails via requireApiKey.
|
|
956
|
+
const isInteractive = !options.prompt && !options.verify && process.stdin.isTTY;
|
|
1053
957
|
// Bootstrap EE auth (loads serverBaseUrl + token from ~/.experience/config.json)
|
|
1054
958
|
const { loadEEAuthToken, getCachedServerBaseUrl } = await import("./ee/auth.js");
|
|
1055
959
|
await loadEEAuthToken().catch(() => { });
|
|
@@ -1253,38 +1157,28 @@ program
|
|
|
1253
1157
|
});
|
|
1254
1158
|
const keys = program
|
|
1255
1159
|
.command("keys")
|
|
1256
|
-
.description("Manage provider API keys
|
|
1160
|
+
.description("Manage provider API keys as environment variables (set, list, delete)");
|
|
1257
1161
|
keys
|
|
1258
1162
|
.command("set <provider>")
|
|
1259
|
-
.description("Prompt for a provider API key and store it
|
|
1260
|
-
.
|
|
1261
|
-
.option("--prefix <prefix>", "Bitwarden item name prefix when --bw is set (default: 'muonroi-cli/')", "muonroi-cli/")
|
|
1262
|
-
.action(async (provider, opts) => {
|
|
1163
|
+
.description("Prompt for a provider API key and store it as an environment variable")
|
|
1164
|
+
.action(async (provider) => {
|
|
1263
1165
|
const { runKeysSet } = await import("./cli/keys.js");
|
|
1264
|
-
await runKeysSet(provider
|
|
1166
|
+
await runKeysSet(provider);
|
|
1265
1167
|
});
|
|
1266
1168
|
keys
|
|
1267
1169
|
.command("list")
|
|
1268
|
-
.description("Show provider keys currently
|
|
1170
|
+
.description("Show provider keys currently set in the environment (masked)")
|
|
1269
1171
|
.action(async () => {
|
|
1270
1172
|
const { runKeysList } = await import("./cli/keys.js");
|
|
1271
1173
|
await runKeysList();
|
|
1272
1174
|
});
|
|
1273
1175
|
keys
|
|
1274
1176
|
.command("delete <provider>")
|
|
1275
|
-
.description("Delete a stored provider key from the
|
|
1177
|
+
.description("Delete a stored provider key from the environment")
|
|
1276
1178
|
.action(async (provider) => {
|
|
1277
1179
|
const { runKeysDelete } = await import("./cli/keys.js");
|
|
1278
1180
|
await runKeysDelete(provider);
|
|
1279
1181
|
});
|
|
1280
|
-
keys
|
|
1281
|
-
.command("import-bw [providers...]")
|
|
1282
|
-
.description("Import keys from a Bitwarden vault into the OS keychain (requires bw CLI + BW_SESSION)")
|
|
1283
|
-
.option("--prefix <prefix>", "Vault item name prefix (default: 'muonroi-cli/')", "muonroi-cli/")
|
|
1284
|
-
.action(async (providers, opts) => {
|
|
1285
|
-
const { runKeysImportBw } = await import("./cli/keys.js");
|
|
1286
|
-
await runKeysImportBw({ providers, itemPrefix: opts.prefix });
|
|
1287
|
-
});
|
|
1288
1182
|
keys
|
|
1289
1183
|
.command("login <provider>")
|
|
1290
1184
|
.description("Log in to a provider via OAuth subscription (supported: openai, xai).")
|
|
@@ -1301,28 +1195,21 @@ keys
|
|
|
1301
1195
|
});
|
|
1302
1196
|
keys
|
|
1303
1197
|
.command("export <file>")
|
|
1304
|
-
.description("Export all
|
|
1198
|
+
.description("Export all provider keys to an encrypted portable bundle (move between devices)")
|
|
1305
1199
|
.action(async (file) => {
|
|
1306
1200
|
const { runKeysExport } = await import("./cli/keys.js");
|
|
1307
1201
|
await runKeysExport(file);
|
|
1308
1202
|
});
|
|
1309
1203
|
keys
|
|
1310
1204
|
.command("import <file>")
|
|
1311
|
-
.description("Import an encrypted bundle
|
|
1205
|
+
.description("Import an encrypted bundle of provider keys (created by 'keys export')")
|
|
1312
1206
|
.action(async (file) => {
|
|
1313
1207
|
const { runKeysImport } = await import("./cli/keys.js");
|
|
1314
1208
|
await runKeysImport(file);
|
|
1315
1209
|
});
|
|
1316
|
-
keys
|
|
1317
|
-
.command("cleanup-settings")
|
|
1318
|
-
.description("Strip plaintext API keys out of ~/.muonroi-cli/user-settings.json after migrating to keychain")
|
|
1319
|
-
.action(async () => {
|
|
1320
|
-
const { runKeysCleanupSettings } = await import("./cli/keys.js");
|
|
1321
|
-
await runKeysCleanupSettings();
|
|
1322
|
-
});
|
|
1323
1210
|
keys
|
|
1324
1211
|
.command("set-chat <id>")
|
|
1325
|
-
.description("Set a chat-service secret (discord-token, discord-guild-id, slack-token, slack-team-id) in the
|
|
1212
|
+
.description("Set a chat-service secret (discord-token, discord-guild-id, slack-token, slack-team-id) in the environment")
|
|
1326
1213
|
.action(async (id) => {
|
|
1327
1214
|
const { runChatKeySet } = await import("./cli/keys.js");
|
|
1328
1215
|
if (!["discord-token", "discord-guild-id", "slack-token", "slack-team-id"].includes(id)) {
|
|
@@ -1342,14 +1229,6 @@ keys
|
|
|
1342
1229
|
}
|
|
1343
1230
|
await runChatKeySet(id, value);
|
|
1344
1231
|
});
|
|
1345
|
-
keys
|
|
1346
|
-
.command("import-bw-chat [ids...]")
|
|
1347
|
-
.option("--prefix <prefix>", "BW item name prefix", "muonroi-cli/chat-")
|
|
1348
|
-
.description("Import chat-service secrets from Bitwarden vault into OS keychain")
|
|
1349
|
-
.action(async (ids, opts) => {
|
|
1350
|
-
const { runChatImportBw } = await import("./cli/keys.js");
|
|
1351
|
-
await runChatImportBw({ ids: ids.length > 0 ? ids : undefined, itemPrefix: opts.prefix });
|
|
1352
|
-
});
|
|
1353
1232
|
const usage = program.command("usage").description("Inspect cost ledger and find spend bloat");
|
|
1354
1233
|
usage
|
|
1355
1234
|
.command("report")
|
|
@@ -1443,20 +1322,10 @@ mcp
|
|
|
1443
1322
|
});
|
|
1444
1323
|
mcp
|
|
1445
1324
|
.command("set <id>")
|
|
1446
|
-
.description("Prompt for an MCP API key (e.g. tavily) and store it
|
|
1447
|
-
.
|
|
1448
|
-
.option("--prefix <prefix>", "Bitwarden item name prefix when --bw is set (default: 'muonroi-cli/')", "muonroi-cli/")
|
|
1449
|
-
.action(async (id, opts) => {
|
|
1325
|
+
.description("Prompt for an MCP API key (e.g. tavily) and store it as an environment variable")
|
|
1326
|
+
.action(async (id) => {
|
|
1450
1327
|
const { runMcpKeysSet } = await import("./cli/keys.js");
|
|
1451
|
-
await runMcpKeysSet(id
|
|
1452
|
-
});
|
|
1453
|
-
mcp
|
|
1454
|
-
.command("import-bw [keys...]")
|
|
1455
|
-
.description("Import MCP secrets (e.g. tavily) from a Bitwarden vault into the OS keychain (requires bw CLI + BW_SESSION)")
|
|
1456
|
-
.option("--prefix <prefix>", "Vault item name prefix (default: 'muonroi-cli/')", "muonroi-cli/")
|
|
1457
|
-
.action(async (keys, opts) => {
|
|
1458
|
-
const { runMcpImportBw } = await import("./cli/keys.js");
|
|
1459
|
-
await runMcpImportBw({ keys, itemPrefix: opts.prefix });
|
|
1328
|
+
await runMcpKeysSet(id);
|
|
1460
1329
|
});
|
|
1461
1330
|
program
|
|
1462
1331
|
.command("mcp-driver")
|
package/dist/src/lsp/builtins.js
CHANGED
|
@@ -342,7 +342,9 @@ async function resolveNodeServerLaunch(root, binary, packageName, baseArgs, auto
|
|
|
342
342
|
}
|
|
343
343
|
if (!autoInstall)
|
|
344
344
|
return null;
|
|
345
|
-
|
|
345
|
+
// Pass `binary` explicitly: a package can ship several bins and the one named
|
|
346
|
+
// after the package is not always the language server (see lspNpmWhich).
|
|
347
|
+
const cached = await lspNpmWhich(packageName, binary);
|
|
346
348
|
if (cached) {
|
|
347
349
|
return { command: cached, args: baseArgs };
|
|
348
350
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { type RuntimeLspServerDefinition } from "./builtins.js";
|
|
2
2
|
import { type LspClientSession } from "./client.js";
|
|
3
|
-
import type { LspDiagnosticFile, LspQueryInput, LspToolResponse, NormalizedLspSettings } from "./types.js";
|
|
3
|
+
import type { ImpactOfChangeResult, LspDiagnosticFile, LspQueryInput, LspQueryResult, LspToolResponse, MutationPreviewResult, NormalizedLspSettings, PolicyAction } from "./types.js";
|
|
4
4
|
interface WorkspaceLspManagerOptions {
|
|
5
5
|
createClient?: (input: {
|
|
6
6
|
serverId: string;
|
|
@@ -13,6 +13,10 @@ export interface WorkspaceLspManager {
|
|
|
13
13
|
touchFile(filePath: string, waitForDiagnostics?: boolean): Promise<LspDiagnosticFile[]>;
|
|
14
14
|
syncFile(filePath: string, content: string, save?: boolean, waitForDiagnostics?: boolean, diagnosticsTimeoutMs?: number): Promise<LspDiagnosticFile[]>;
|
|
15
15
|
query(input: LspQueryInput): Promise<LspToolResponse>;
|
|
16
|
+
waitForDiagnostics(filePath: string, timeout: number): Promise<LspQueryResult>;
|
|
17
|
+
impactOfChange(filePath: string, query?: string, timeout?: number): Promise<ImpactOfChangeResult>;
|
|
18
|
+
lspMutationPreview(filePath: string, change: string): Promise<MutationPreviewResult>;
|
|
19
|
+
lspBeforeGrep(filePath: string, query?: string): Promise<PolicyAction>;
|
|
16
20
|
close(): Promise<void>;
|
|
17
21
|
}
|
|
18
22
|
export declare function createWorkspaceLspManager(cwd: string, settings: NormalizedLspSettings, options?: WorkspaceLspManagerOptions): WorkspaceLspManager;
|