noumen 0.1.0 → 0.2.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/README.md +767 -51
- package/dist/a2a/index.d.ts +148 -0
- package/dist/a2a/index.js +579 -0
- package/dist/a2a/index.js.map +1 -0
- package/dist/acp/index.d.ts +129 -0
- package/dist/acp/index.js +498 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/agent-BrkbZyOT.d.ts +1028 -0
- package/dist/cache-DVqaCX8v.d.ts +38 -0
- package/dist/chunk-2ZTGQLYK.js +356 -0
- package/dist/chunk-2ZTGQLYK.js.map +1 -0
- package/dist/chunk-42PHHZUA.js +132 -0
- package/dist/chunk-42PHHZUA.js.map +1 -0
- package/dist/chunk-4SQA2UCV.js +26 -0
- package/dist/chunk-4SQA2UCV.js.map +1 -0
- package/dist/chunk-5GEX6ZSB.js +179 -0
- package/dist/chunk-5GEX6ZSB.js.map +1 -0
- package/dist/chunk-7ZMN7XJE.js +94 -0
- package/dist/chunk-7ZMN7XJE.js.map +1 -0
- package/dist/chunk-AMYIJSAZ.js +57 -0
- package/dist/chunk-AMYIJSAZ.js.map +1 -0
- package/dist/chunk-BGG2E6JD.js +10 -0
- package/dist/chunk-BGG2E6JD.js.map +1 -0
- package/dist/chunk-BZSFUEWM.js +43 -0
- package/dist/chunk-BZSFUEWM.js.map +1 -0
- package/dist/chunk-CPFHEPW4.js +139 -0
- package/dist/chunk-CPFHEPW4.js.map +1 -0
- package/dist/chunk-D43BWEZA.js +346 -0
- package/dist/chunk-D43BWEZA.js.map +1 -0
- package/dist/chunk-DGUM43GV.js +11 -0
- package/dist/chunk-DGUM43GV.js.map +1 -0
- package/dist/chunk-JACGEMTF.js +43 -0
- package/dist/chunk-JACGEMTF.js.map +1 -0
- package/dist/chunk-JX7CLUCV.js +21 -0
- package/dist/chunk-JX7CLUCV.js.map +1 -0
- package/dist/chunk-KXDB56YW.js +39 -0
- package/dist/chunk-KXDB56YW.js.map +1 -0
- package/dist/chunk-KY6ZPWHO.js +112 -0
- package/dist/chunk-KY6ZPWHO.js.map +1 -0
- package/dist/chunk-NBDFQYUZ.js +7992 -0
- package/dist/chunk-NBDFQYUZ.js.map +1 -0
- package/dist/chunk-OGXNFXFA.js +196 -0
- package/dist/chunk-OGXNFXFA.js.map +1 -0
- package/dist/chunk-QTJ7VTJY.js +1994 -0
- package/dist/chunk-QTJ7VTJY.js.map +1 -0
- package/dist/chunk-UVSSQBDY.js +192 -0
- package/dist/chunk-UVSSQBDY.js.map +1 -0
- package/dist/chunk-Y45R3PQL.js +684 -0
- package/dist/chunk-Y45R3PQL.js.map +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +868 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/client/index.d.ts +64 -0
- package/dist/client/index.js +409 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client-CRRO2376.js +10 -0
- package/dist/client-CRRO2376.js.map +1 -0
- package/dist/headless-Q7XHHZIW.js +143 -0
- package/dist/headless-Q7XHHZIW.js.map +1 -0
- package/dist/history-snip-64GYP4ZL.js +12 -0
- package/dist/history-snip-64GYP4ZL.js.map +1 -0
- package/dist/index.d.ts +1305 -418
- package/dist/index.js +384 -1757
- package/dist/index.js.map +1 -1
- package/dist/jsonrpc/index.d.ts +54 -0
- package/dist/jsonrpc/index.js +34 -0
- package/dist/jsonrpc/index.js.map +1 -0
- package/dist/lsp/index.d.ts +36 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/index.js.map +1 -0
- package/dist/lsp-PS3BWIHC.js +8 -0
- package/dist/lsp-PS3BWIHC.js.map +1 -0
- package/dist/manager-DLXK63XC.js +8 -0
- package/dist/manager-DLXK63XC.js.map +1 -0
- package/dist/mcp/index.d.ts +111 -0
- package/dist/mcp/index.js +104 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp-auth-AEI2R4ZC.js +9 -0
- package/dist/mcp-auth-AEI2R4ZC.js.map +1 -0
- package/dist/ollama-YNXAYP3R.js +18 -0
- package/dist/ollama-YNXAYP3R.js.map +1 -0
- package/dist/provider-factory-34MSWJZ3.js +20 -0
- package/dist/provider-factory-34MSWJZ3.js.map +1 -0
- package/dist/providers/anthropic.d.ts +19 -0
- package/dist/providers/anthropic.js +33 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/bedrock.d.ts +39 -0
- package/dist/providers/bedrock.js +54 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/gemini.d.ts +16 -0
- package/dist/providers/gemini.js +224 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/openai.d.ts +18 -0
- package/dist/providers/openai.js +8 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/providers/openrouter.d.ts +16 -0
- package/dist/providers/openrouter.js +23 -0
- package/dist/providers/openrouter.js.map +1 -0
- package/dist/providers/vertex.d.ts +40 -0
- package/dist/providers/vertex.js +64 -0
- package/dist/providers/vertex.js.map +1 -0
- package/dist/render-GRN4ZSSW.js +14 -0
- package/dist/render-GRN4ZSSW.js.map +1 -0
- package/dist/resolve-XM52G7YE.js +14 -0
- package/dist/resolve-XM52G7YE.js.map +1 -0
- package/dist/server/index.d.ts +128 -0
- package/dist/server/index.js +626 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server-Cg1yWGaV.d.ts +96 -0
- package/dist/spinner-OJNR6NFO.js +8 -0
- package/dist/spinner-OJNR6NFO.js.map +1 -0
- package/dist/types-2kTLUCnD.d.ts +107 -0
- package/dist/types-3c88cRKH.d.ts +547 -0
- package/dist/types-CwKKucOF.d.ts +620 -0
- package/dist/types-DwdzmXfs.d.ts +107 -0
- package/dist/types-NIyVwQ4h.d.ts +109 -0
- package/dist/types-QwfylltH.d.ts +71 -0
- package/package.json +134 -6
|
@@ -0,0 +1,868 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
startSpinner
|
|
4
|
+
} from "../chunk-KXDB56YW.js";
|
|
5
|
+
import {
|
|
6
|
+
isOllamaRunning,
|
|
7
|
+
ollamaBaseURL
|
|
8
|
+
} from "../chunk-JX7CLUCV.js";
|
|
9
|
+
import {
|
|
10
|
+
createRenderState,
|
|
11
|
+
isVisibleEvent,
|
|
12
|
+
promptPermission,
|
|
13
|
+
renderEvent
|
|
14
|
+
} from "../chunk-OGXNFXFA.js";
|
|
15
|
+
import {
|
|
16
|
+
Agent,
|
|
17
|
+
LocalSandbox,
|
|
18
|
+
UnsandboxedLocal
|
|
19
|
+
} from "../chunk-NBDFQYUZ.js";
|
|
20
|
+
import {
|
|
21
|
+
DEFAULT_MODELS,
|
|
22
|
+
SUPPORTED_PROVIDERS,
|
|
23
|
+
detectProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "../chunk-7ZMN7XJE.js";
|
|
26
|
+
import "../chunk-KY6ZPWHO.js";
|
|
27
|
+
import "../chunk-42PHHZUA.js";
|
|
28
|
+
import "../chunk-BGG2E6JD.js";
|
|
29
|
+
import "../chunk-QTJ7VTJY.js";
|
|
30
|
+
import "../chunk-5GEX6ZSB.js";
|
|
31
|
+
import "../chunk-4SQA2UCV.js";
|
|
32
|
+
import "../chunk-JACGEMTF.js";
|
|
33
|
+
import "../chunk-DGUM43GV.js";
|
|
34
|
+
|
|
35
|
+
// src/cli/index.ts
|
|
36
|
+
import { Command } from "commander";
|
|
37
|
+
import chalk3 from "chalk";
|
|
38
|
+
|
|
39
|
+
// src/cli/config.ts
|
|
40
|
+
import * as fs from "fs";
|
|
41
|
+
import * as path from "path";
|
|
42
|
+
import * as os from "os";
|
|
43
|
+
function loadGlobalConfig() {
|
|
44
|
+
const globalPath = path.join(os.homedir(), ".noumen", "config.json");
|
|
45
|
+
try {
|
|
46
|
+
const raw = fs.readFileSync(globalPath, "utf-8");
|
|
47
|
+
return JSON.parse(raw);
|
|
48
|
+
} catch {
|
|
49
|
+
return {};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function loadProjectConfig(cwd) {
|
|
53
|
+
let dir = path.resolve(cwd);
|
|
54
|
+
const root = path.parse(dir).root;
|
|
55
|
+
while (true) {
|
|
56
|
+
const candidate = path.join(dir, ".noumen", "config.json");
|
|
57
|
+
try {
|
|
58
|
+
const raw = fs.readFileSync(candidate, "utf-8");
|
|
59
|
+
return JSON.parse(raw);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
const parent = path.dirname(dir);
|
|
63
|
+
if (parent === dir || dir === root) break;
|
|
64
|
+
dir = parent;
|
|
65
|
+
}
|
|
66
|
+
return {};
|
|
67
|
+
}
|
|
68
|
+
function loadCliConfig(cwd) {
|
|
69
|
+
const global = loadGlobalConfig();
|
|
70
|
+
const project = loadProjectConfig(cwd);
|
|
71
|
+
return { ...global, ...project };
|
|
72
|
+
}
|
|
73
|
+
function mergeConfig(config, flags) {
|
|
74
|
+
const cwd = flags.cwd ?? process.cwd();
|
|
75
|
+
return {
|
|
76
|
+
...config,
|
|
77
|
+
...flags.provider !== void 0 && { provider: flags.provider },
|
|
78
|
+
...flags.model !== void 0 && { model: flags.model },
|
|
79
|
+
...flags.apiKey !== void 0 && { apiKey: flags.apiKey },
|
|
80
|
+
...flags.baseUrl !== void 0 && { baseURL: flags.baseUrl },
|
|
81
|
+
...flags.permission !== void 0 && { permissions: flags.permission },
|
|
82
|
+
...flags.thinking !== void 0 && { thinking: flags.thinking },
|
|
83
|
+
...flags.maxTurns !== void 0 && { maxTurns: flags.maxTurns },
|
|
84
|
+
...flags.systemPrompt !== void 0 && { systemPrompt: flags.systemPrompt },
|
|
85
|
+
cwd,
|
|
86
|
+
json: flags.json,
|
|
87
|
+
quiet: flags.quiet,
|
|
88
|
+
verbose: flags.verbose,
|
|
89
|
+
headless: flags.headless,
|
|
90
|
+
prompt: flags.prompt,
|
|
91
|
+
noSandbox: flags.sandbox === false ? true : void 0,
|
|
92
|
+
sandboxAllowWrite: flags.sandboxAllowWrite,
|
|
93
|
+
sandboxAllowDomain: flags.sandboxAllowDomain
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// src/cli/repl.ts
|
|
98
|
+
import * as readline from "readline/promises";
|
|
99
|
+
import chalk from "chalk";
|
|
100
|
+
async function startRepl(code, config) {
|
|
101
|
+
const rl = readline.createInterface({
|
|
102
|
+
input: process.stdin,
|
|
103
|
+
output: process.stderr,
|
|
104
|
+
terminal: true
|
|
105
|
+
});
|
|
106
|
+
let thread = code.createThread({
|
|
107
|
+
userInputHandler: (q) => promptPermission(rl, "agent", q).then((ok) => ok ? "yes" : "no")
|
|
108
|
+
});
|
|
109
|
+
let runningTurn = false;
|
|
110
|
+
let currentThread = thread;
|
|
111
|
+
const sigintHandler = () => {
|
|
112
|
+
if (runningTurn) {
|
|
113
|
+
currentThread.abort();
|
|
114
|
+
runningTurn = false;
|
|
115
|
+
process.stderr.write(chalk.yellow("\n Cancelled.\n\n"));
|
|
116
|
+
} else {
|
|
117
|
+
process.stderr.write(chalk.dim("\nGoodbye.\n"));
|
|
118
|
+
rl.close();
|
|
119
|
+
process.exit(0);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
process.on("SIGINT", sigintHandler);
|
|
123
|
+
printWelcome(config);
|
|
124
|
+
try {
|
|
125
|
+
while (true) {
|
|
126
|
+
let input;
|
|
127
|
+
try {
|
|
128
|
+
input = await rl.question(chalk.blue("> "));
|
|
129
|
+
} catch {
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (!input.trim()) continue;
|
|
133
|
+
if (input.startsWith("/")) {
|
|
134
|
+
const result = await handleSlashCommand(
|
|
135
|
+
input,
|
|
136
|
+
thread,
|
|
137
|
+
code,
|
|
138
|
+
config,
|
|
139
|
+
rl
|
|
140
|
+
);
|
|
141
|
+
if (result === "quit") break;
|
|
142
|
+
if (result === "new") {
|
|
143
|
+
thread = code.createThread({
|
|
144
|
+
userInputHandler: (q) => promptPermission(rl, "agent", q).then((ok) => ok ? "yes" : "no")
|
|
145
|
+
});
|
|
146
|
+
currentThread = thread;
|
|
147
|
+
}
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
const state = createRenderState();
|
|
151
|
+
const runOpts = config.maxTurns ? { maxTurns: config.maxTurns } : void 0;
|
|
152
|
+
const spinner = !config.json && !config.quiet ? startSpinner("Thinking") : null;
|
|
153
|
+
runningTurn = true;
|
|
154
|
+
try {
|
|
155
|
+
for await (const event of thread.run(input, runOpts)) {
|
|
156
|
+
if (!state.showedActivity && spinner && isVisibleEvent(event, config)) {
|
|
157
|
+
spinner.stop();
|
|
158
|
+
state.showedActivity = true;
|
|
159
|
+
}
|
|
160
|
+
renderEvent(event, config, state);
|
|
161
|
+
}
|
|
162
|
+
} catch (err) {
|
|
163
|
+
if (err?.name === "AbortError") {
|
|
164
|
+
} else {
|
|
165
|
+
throw err;
|
|
166
|
+
}
|
|
167
|
+
} finally {
|
|
168
|
+
spinner?.stop();
|
|
169
|
+
runningTurn = false;
|
|
170
|
+
}
|
|
171
|
+
if (state.accumulatedText && !state.accumulatedText.endsWith("\n")) {
|
|
172
|
+
process.stdout.write("\n");
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
process.removeListener("SIGINT", sigintHandler);
|
|
177
|
+
rl.close();
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
function printWelcome(config) {
|
|
181
|
+
const provider = config.provider ?? "auto";
|
|
182
|
+
const model = config.model ?? DEFAULT_MODELS[provider] ?? "default";
|
|
183
|
+
process.stderr.write(
|
|
184
|
+
chalk.bold("noumen") + chalk.dim(` \u2014 ${provider}/${model}`) + "\n" + chalk.dim("Type a message to begin. /help for commands, Ctrl+C to cancel.") + "\n\n"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
async function handleSlashCommand(input, thread, code, config, _rl) {
|
|
188
|
+
const [cmd] = input.trim().split(/\s+/);
|
|
189
|
+
switch (cmd) {
|
|
190
|
+
case "/quit":
|
|
191
|
+
case "/exit":
|
|
192
|
+
case "/q":
|
|
193
|
+
process.stderr.write(chalk.dim("Goodbye.\n"));
|
|
194
|
+
return "quit";
|
|
195
|
+
case "/new":
|
|
196
|
+
process.stderr.write(chalk.dim("Starting new conversation.\n\n"));
|
|
197
|
+
return "new";
|
|
198
|
+
case "/session":
|
|
199
|
+
process.stderr.write(chalk.dim(`Session: ${thread.sessionId}
|
|
200
|
+
`));
|
|
201
|
+
return "continue";
|
|
202
|
+
case "/cost": {
|
|
203
|
+
const summary = code.getCostSummary();
|
|
204
|
+
if (summary) {
|
|
205
|
+
process.stderr.write(
|
|
206
|
+
chalk.dim(
|
|
207
|
+
`Cost: $${summary.totalCostUSD.toFixed(4)} | Input: ${summary.totalInputTokens} tokens | Output: ${summary.totalOutputTokens} tokens
|
|
208
|
+
`
|
|
209
|
+
)
|
|
210
|
+
);
|
|
211
|
+
} else {
|
|
212
|
+
process.stderr.write(chalk.dim("Cost tracking not enabled.\n"));
|
|
213
|
+
}
|
|
214
|
+
return "continue";
|
|
215
|
+
}
|
|
216
|
+
case "/sessions": {
|
|
217
|
+
const sessions = await code.listSessions();
|
|
218
|
+
if (sessions.length === 0) {
|
|
219
|
+
process.stderr.write(chalk.dim("No saved sessions.\n"));
|
|
220
|
+
} else {
|
|
221
|
+
for (const s of sessions.slice(0, 20)) {
|
|
222
|
+
process.stderr.write(
|
|
223
|
+
chalk.dim(
|
|
224
|
+
` ${s.sessionId.slice(0, 8)} ${s.createdAt ?? ""} ${s.messageCount ?? 0} messages
|
|
225
|
+
`
|
|
226
|
+
)
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return "continue";
|
|
231
|
+
}
|
|
232
|
+
case "/model": {
|
|
233
|
+
const arg = input.trim().split(/\s+/).slice(1).join(" ");
|
|
234
|
+
if (!arg) {
|
|
235
|
+
process.stderr.write(chalk.dim(`Current model: ${thread.getModel()}
|
|
236
|
+
`));
|
|
237
|
+
} else {
|
|
238
|
+
thread.setModel(arg);
|
|
239
|
+
process.stderr.write(chalk.dim(`Model set to ${arg}
|
|
240
|
+
`));
|
|
241
|
+
}
|
|
242
|
+
return "continue";
|
|
243
|
+
}
|
|
244
|
+
case "/provider": {
|
|
245
|
+
const parts = input.trim().split(/\s+/).slice(1);
|
|
246
|
+
const providerName = parts[0];
|
|
247
|
+
const modelArg = parts[1];
|
|
248
|
+
if (!providerName) {
|
|
249
|
+
process.stderr.write(
|
|
250
|
+
chalk.dim(`Current: ${config.provider ?? "auto"}/${thread.getModel()}
|
|
251
|
+
`) + chalk.dim(`Available: ${SUPPORTED_PROVIDERS.join(", ")}
|
|
252
|
+
`)
|
|
253
|
+
);
|
|
254
|
+
return "continue";
|
|
255
|
+
}
|
|
256
|
+
if (!SUPPORTED_PROVIDERS.includes(providerName)) {
|
|
257
|
+
process.stderr.write(chalk.red(`Unknown provider: ${providerName}. Available: ${SUPPORTED_PROVIDERS.join(", ")}
|
|
258
|
+
`));
|
|
259
|
+
return "continue";
|
|
260
|
+
}
|
|
261
|
+
try {
|
|
262
|
+
const model = modelArg ?? DEFAULT_MODELS[providerName];
|
|
263
|
+
const provider = await resolveProvider(providerName, {
|
|
264
|
+
apiKey: config.apiKey,
|
|
265
|
+
model,
|
|
266
|
+
baseURL: config.baseURL
|
|
267
|
+
});
|
|
268
|
+
thread.setProvider(provider, model);
|
|
269
|
+
config.provider = providerName;
|
|
270
|
+
config.model = model;
|
|
271
|
+
process.stderr.write(chalk.dim(`Switched to ${providerName}/${model}
|
|
272
|
+
`));
|
|
273
|
+
} catch (err) {
|
|
274
|
+
process.stderr.write(chalk.red(`Failed to switch: ${err.message}
|
|
275
|
+
`));
|
|
276
|
+
}
|
|
277
|
+
return "continue";
|
|
278
|
+
}
|
|
279
|
+
case "/verbose":
|
|
280
|
+
config.verbose = !config.verbose;
|
|
281
|
+
process.stderr.write(
|
|
282
|
+
chalk.dim(`Verbose mode: ${config.verbose ? "on" : "off"}
|
|
283
|
+
`)
|
|
284
|
+
);
|
|
285
|
+
return "continue";
|
|
286
|
+
case "/help":
|
|
287
|
+
process.stderr.write(
|
|
288
|
+
chalk.dim(
|
|
289
|
+
[
|
|
290
|
+
"Commands:",
|
|
291
|
+
" /quit, /exit Exit the REPL",
|
|
292
|
+
" /new Start a new conversation",
|
|
293
|
+
" /model [name] Show or change the model",
|
|
294
|
+
" /provider [name] Show or switch provider (and model)",
|
|
295
|
+
" /session Show current session ID",
|
|
296
|
+
" /sessions List saved sessions",
|
|
297
|
+
" /cost Show token usage and cost",
|
|
298
|
+
" /verbose Toggle verbose output",
|
|
299
|
+
" /help Show this help",
|
|
300
|
+
"",
|
|
301
|
+
"Shortcuts:",
|
|
302
|
+
" Ctrl+C Cancel current turn / exit when idle",
|
|
303
|
+
""
|
|
304
|
+
].join("\n")
|
|
305
|
+
)
|
|
306
|
+
);
|
|
307
|
+
return "continue";
|
|
308
|
+
default:
|
|
309
|
+
process.stderr.write(
|
|
310
|
+
chalk.yellow(`Unknown command: ${cmd}. Try /help
|
|
311
|
+
`)
|
|
312
|
+
);
|
|
313
|
+
return "continue";
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// src/cli/init.ts
|
|
318
|
+
import * as fs2 from "fs";
|
|
319
|
+
import * as path2 from "path";
|
|
320
|
+
import * as readline2 from "readline/promises";
|
|
321
|
+
import chalk2 from "chalk";
|
|
322
|
+
var PERMISSION_MODES = ["default", "plan", "acceptEdits", "auto", "bypassPermissions"];
|
|
323
|
+
async function runInit() {
|
|
324
|
+
const rl = readline2.createInterface({
|
|
325
|
+
input: process.stdin,
|
|
326
|
+
output: process.stdout
|
|
327
|
+
});
|
|
328
|
+
try {
|
|
329
|
+
process.stdout.write(chalk2.bold("noumen init") + "\n\n");
|
|
330
|
+
const provider = await askChoice(
|
|
331
|
+
rl,
|
|
332
|
+
"Provider",
|
|
333
|
+
SUPPORTED_PROVIDERS,
|
|
334
|
+
"anthropic"
|
|
335
|
+
);
|
|
336
|
+
let defaultModel = DEFAULT_MODELS[provider] ?? "";
|
|
337
|
+
if (provider === "ollama") {
|
|
338
|
+
const ollamaModels = await listOllamaModels();
|
|
339
|
+
if (ollamaModels.length > 0) {
|
|
340
|
+
process.stdout.write(chalk2.dim(` Available models: ${ollamaModels.join(", ")}
|
|
341
|
+
`));
|
|
342
|
+
if (ollamaModels.includes(defaultModel)) {
|
|
343
|
+
} else {
|
|
344
|
+
defaultModel = ollamaModels[0];
|
|
345
|
+
}
|
|
346
|
+
} else {
|
|
347
|
+
process.stdout.write(
|
|
348
|
+
chalk2.yellow(
|
|
349
|
+
`
|
|
350
|
+
Ollama doesn't appear to be running at ${ollamaBaseURL()}.
|
|
351
|
+
Install it from https://ollama.com, then run:
|
|
352
|
+
ollama pull ${defaultModel}
|
|
353
|
+
ollama serve
|
|
354
|
+
|
|
355
|
+
`
|
|
356
|
+
)
|
|
357
|
+
);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const model = await askDefault(rl, "Model", defaultModel);
|
|
361
|
+
const permissions = await askChoice(
|
|
362
|
+
rl,
|
|
363
|
+
"Permission mode",
|
|
364
|
+
PERMISSION_MODES,
|
|
365
|
+
"default"
|
|
366
|
+
);
|
|
367
|
+
const config = { provider };
|
|
368
|
+
if (model !== defaultModel) config.model = model;
|
|
369
|
+
if (permissions !== "default") config.permissions = permissions;
|
|
370
|
+
const dir = path2.join(process.cwd(), ".noumen");
|
|
371
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
372
|
+
const configPath = path2.join(dir, "config.json");
|
|
373
|
+
fs2.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
374
|
+
process.stdout.write(chalk2.green(` Created ${configPath}
|
|
375
|
+
`));
|
|
376
|
+
const noumenMdPath = path2.join(process.cwd(), "NOUMEN.md");
|
|
377
|
+
if (!fs2.existsSync(noumenMdPath)) {
|
|
378
|
+
const create = await askDefault(rl, "Create NOUMEN.md?", "Y");
|
|
379
|
+
if (create.toLowerCase() === "y" || create.toLowerCase() === "yes") {
|
|
380
|
+
fs2.writeFileSync(noumenMdPath, NOUMEN_MD_TEMPLATE);
|
|
381
|
+
process.stdout.write(chalk2.green(` Created ${noumenMdPath}
|
|
382
|
+
`));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
process.stdout.write(
|
|
386
|
+
"\n" + chalk2.dim("Run `noumen` to start a session.") + "\n"
|
|
387
|
+
);
|
|
388
|
+
} finally {
|
|
389
|
+
rl.close();
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
async function listOllamaModels() {
|
|
393
|
+
if (!await isOllamaRunning()) return [];
|
|
394
|
+
try {
|
|
395
|
+
const res = await fetch(`${ollamaBaseURL()}/api/tags`, {
|
|
396
|
+
signal: AbortSignal.timeout(2e3)
|
|
397
|
+
});
|
|
398
|
+
if (!res.ok) return [];
|
|
399
|
+
const data = await res.json();
|
|
400
|
+
return (data.models ?? []).map((m) => m.name);
|
|
401
|
+
} catch {
|
|
402
|
+
return [];
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
async function askChoice(rl, label, choices, defaultValue) {
|
|
406
|
+
const hint = choices.join(", ");
|
|
407
|
+
const answer = await rl.question(
|
|
408
|
+
` ${label} (${hint}) [${chalk2.bold(defaultValue)}]: `
|
|
409
|
+
);
|
|
410
|
+
const trimmed = answer.trim();
|
|
411
|
+
if (!trimmed) return defaultValue;
|
|
412
|
+
if (choices.includes(trimmed)) return trimmed;
|
|
413
|
+
process.stdout.write(chalk2.yellow(` Invalid choice, using ${defaultValue}
|
|
414
|
+
`));
|
|
415
|
+
return defaultValue;
|
|
416
|
+
}
|
|
417
|
+
async function askDefault(rl, label, defaultValue) {
|
|
418
|
+
const answer = await rl.question(
|
|
419
|
+
` ${label} [${chalk2.bold(defaultValue)}]: `
|
|
420
|
+
);
|
|
421
|
+
return answer.trim() || defaultValue;
|
|
422
|
+
}
|
|
423
|
+
var NOUMEN_MD_TEMPLATE = `# Project Instructions
|
|
424
|
+
|
|
425
|
+
Add project-specific instructions for the AI agent here.
|
|
426
|
+
These instructions are loaded automatically when running noumen in this directory.
|
|
427
|
+
|
|
428
|
+
## Guidelines
|
|
429
|
+
|
|
430
|
+
- Describe your project's coding conventions
|
|
431
|
+
- Note important architectural decisions
|
|
432
|
+
- List files or patterns the agent should be aware of
|
|
433
|
+
`;
|
|
434
|
+
|
|
435
|
+
// src/cli/index.ts
|
|
436
|
+
import * as os2 from "os";
|
|
437
|
+
var VERSION = "0.1.0";
|
|
438
|
+
async function listLocalOllamaModels(baseURL) {
|
|
439
|
+
try {
|
|
440
|
+
const res = await fetch(`${baseURL}/api/tags`, {
|
|
441
|
+
signal: AbortSignal.timeout(2e3)
|
|
442
|
+
});
|
|
443
|
+
if (!res.ok) return [];
|
|
444
|
+
const data = await res.json();
|
|
445
|
+
return (data.models ?? []).map((m) => m.name);
|
|
446
|
+
} catch {
|
|
447
|
+
return [];
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
function parseThinking(level) {
|
|
451
|
+
if (!level || level === "off") return { type: "disabled" };
|
|
452
|
+
const budgets = {
|
|
453
|
+
low: 1024,
|
|
454
|
+
medium: 10240,
|
|
455
|
+
high: 32768
|
|
456
|
+
};
|
|
457
|
+
const budget = budgets[level];
|
|
458
|
+
if (budget) return { type: "enabled", budgetTokens: budget };
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
function createCliSandbox(config) {
|
|
462
|
+
if (config.noSandbox) {
|
|
463
|
+
return UnsandboxedLocal({ cwd: config.cwd });
|
|
464
|
+
}
|
|
465
|
+
const sandboxOpts = {
|
|
466
|
+
cwd: config.cwd
|
|
467
|
+
};
|
|
468
|
+
const allowWrite = config.sandboxAllowWrite ? config.sandboxAllowWrite.split(",").map((s) => s.trim()) : void 0;
|
|
469
|
+
const allowDomains = config.sandboxAllowDomain ? config.sandboxAllowDomain.split(",").map((s) => s.trim()) : void 0;
|
|
470
|
+
if (allowWrite || allowDomains) {
|
|
471
|
+
sandboxOpts.sandbox = {
|
|
472
|
+
filesystem: allowWrite ? { allowWrite: [config.cwd, ...allowWrite] } : void 0,
|
|
473
|
+
network: allowDomains ? { allowedDomains: allowDomains } : void 0
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
return LocalSandbox(sandboxOpts);
|
|
477
|
+
}
|
|
478
|
+
async function readStdin() {
|
|
479
|
+
const chunks = [];
|
|
480
|
+
for await (const chunk of process.stdin) {
|
|
481
|
+
chunks.push(chunk);
|
|
482
|
+
}
|
|
483
|
+
return Buffer.concat(chunks).toString("utf-8").trim();
|
|
484
|
+
}
|
|
485
|
+
async function main() {
|
|
486
|
+
const program = new Command("noumen").version(VERSION).description("AI coding agent \u2014 bring your own provider").option("-p, --provider <name>", "openai | anthropic | gemini | openrouter | bedrock | vertex | ollama").option("-m, --model <model>", "model name").option("--api-key <key>", "API key (overrides env vars)").option("--base-url <url>", "override provider base URL").option("--cwd <dir>", "working directory").option("--permission <mode>", "permission mode (default, plan, acceptEdits, auto, bypassPermissions, dontAsk)").option("--thinking <level>", "thinking level: off, low, medium, high").option("--max-turns <n>", "max agent turns", parseInt).option("--json", "emit JSONL stream events to stdout").option("--quiet", "only output final text").option("--verbose", "show tool calls and thinking").option("--headless", "NDJSON stdin/stdout protocol for programmatic control").option("--no-sandbox", "disable OS-level sandboxing (use UnsandboxedLocal)").option("--sandbox-allow-write <paths>", "comma-separated paths to allow writing in sandbox").option("--sandbox-allow-domain <domains>", "comma-separated domains to allow in sandbox").option("-c, --prompt <text>", "one-shot prompt (non-interactive)").argument("[prompt...]", "inline prompt").allowExcessArguments(true).action(async (args) => {
|
|
487
|
+
const opts = program.opts();
|
|
488
|
+
if (args.length > 0 && !opts.prompt) {
|
|
489
|
+
opts.prompt = args.join(" ");
|
|
490
|
+
}
|
|
491
|
+
if (!process.stdin.isTTY && !opts.prompt) {
|
|
492
|
+
opts.prompt = await readStdin();
|
|
493
|
+
if (!opts.prompt) {
|
|
494
|
+
process.stderr.write(chalk3.red("No input provided.\n"));
|
|
495
|
+
process.exit(1);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
const cwd = opts.cwd ?? process.cwd();
|
|
499
|
+
const config = loadCliConfig(cwd);
|
|
500
|
+
const merged = mergeConfig(config, opts);
|
|
501
|
+
await runAgent(merged);
|
|
502
|
+
});
|
|
503
|
+
program.command("init").description("create .noumen/config.json in the current directory").action(async () => {
|
|
504
|
+
await runInit();
|
|
505
|
+
process.exit(0);
|
|
506
|
+
});
|
|
507
|
+
program.command("sessions").description("list past sessions").action(async () => {
|
|
508
|
+
await listSessions();
|
|
509
|
+
process.exit(0);
|
|
510
|
+
});
|
|
511
|
+
program.command("resume <session-id>").description("resume a previous session").action(async (sessionId) => {
|
|
512
|
+
await resumeSession(sessionId);
|
|
513
|
+
});
|
|
514
|
+
program.command("doctor").description("run health checks on provider, sandbox, MCP, and LSP").action(async () => {
|
|
515
|
+
await runDoctor();
|
|
516
|
+
});
|
|
517
|
+
await program.parseAsync(process.argv);
|
|
518
|
+
}
|
|
519
|
+
async function runAgent(config) {
|
|
520
|
+
const providerName = config.provider ?? await detectProvider();
|
|
521
|
+
if (!providerName) {
|
|
522
|
+
if (!process.stdin.isTTY) {
|
|
523
|
+
process.stderr.write(chalk3.red("No provider specified.\n"));
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
process.stderr.write(
|
|
527
|
+
chalk3.bold("Welcome to noumen!\n\n") + chalk3.dim("No provider detected. Let's set one up.\n\n")
|
|
528
|
+
);
|
|
529
|
+
const { createInterface: createInterface3 } = await import("readline/promises");
|
|
530
|
+
const rl = createInterface3({ input: process.stdin, output: process.stderr, terminal: true });
|
|
531
|
+
try {
|
|
532
|
+
const { SUPPORTED_PROVIDERS: SUPPORTED_PROVIDERS2, isOllamaRunning: isOllamaRunning2, ollamaBaseURL: ollamaBaseURL2 } = await import("../provider-factory-34MSWJZ3.js");
|
|
533
|
+
const providerAnswer = await rl.question(
|
|
534
|
+
` Provider (${SUPPORTED_PROVIDERS2.join(", ")}) [${chalk3.bold("ollama")}]: `
|
|
535
|
+
);
|
|
536
|
+
const picked = providerAnswer.trim() || "ollama";
|
|
537
|
+
if (!SUPPORTED_PROVIDERS2.includes(picked)) {
|
|
538
|
+
process.stderr.write(chalk3.red(`Unknown provider: ${picked}
|
|
539
|
+
`));
|
|
540
|
+
process.exit(1);
|
|
541
|
+
}
|
|
542
|
+
if (picked === "ollama") {
|
|
543
|
+
if (!await isOllamaRunning2()) {
|
|
544
|
+
process.stderr.write(
|
|
545
|
+
chalk3.yellow(`
|
|
546
|
+
Ollama doesn't appear to be running at ${ollamaBaseURL2()}.
|
|
547
|
+
`) + chalk3.yellow(` Install it from https://ollama.com, then run:
|
|
548
|
+
|
|
549
|
+
`) + ` ${chalk3.cyan("ollama pull qwen2.5-coder:32b")}
|
|
550
|
+
${chalk3.cyan("ollama serve")}
|
|
551
|
+
|
|
552
|
+
` + chalk3.yellow(` Then re-run noumen.
|
|
553
|
+
`)
|
|
554
|
+
);
|
|
555
|
+
rl.close();
|
|
556
|
+
process.exit(1);
|
|
557
|
+
}
|
|
558
|
+
const models = await listLocalOllamaModels(ollamaBaseURL2());
|
|
559
|
+
if (models.length > 0) {
|
|
560
|
+
process.stderr.write(chalk3.dim(` Available models: ${models.join(", ")}
|
|
561
|
+
`));
|
|
562
|
+
const defaultModel = models.includes("qwen2.5-coder:32b") ? "qwen2.5-coder:32b" : models[0];
|
|
563
|
+
const modelAnswer = await rl.question(
|
|
564
|
+
` Model [${chalk3.bold(defaultModel)}]: `
|
|
565
|
+
);
|
|
566
|
+
config.model = modelAnswer.trim() || defaultModel;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
const needsKey = !["bedrock", "vertex", "ollama"].includes(picked);
|
|
570
|
+
let apiKey;
|
|
571
|
+
if (needsKey) {
|
|
572
|
+
const keyAnswer = await rl.question(` API key: `);
|
|
573
|
+
apiKey = keyAnswer.trim();
|
|
574
|
+
if (!apiKey) {
|
|
575
|
+
process.stderr.write(chalk3.red("API key is required.\n"));
|
|
576
|
+
process.exit(1);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
rl.close();
|
|
580
|
+
config.provider = picked;
|
|
581
|
+
if (apiKey) config.apiKey = apiKey;
|
|
582
|
+
} catch {
|
|
583
|
+
rl.close();
|
|
584
|
+
process.exit(1);
|
|
585
|
+
}
|
|
586
|
+
return runAgent(config);
|
|
587
|
+
}
|
|
588
|
+
if (!config.model) {
|
|
589
|
+
const { DEFAULT_MODELS: DEFAULT_MODELS2 } = await import("../provider-factory-34MSWJZ3.js");
|
|
590
|
+
if (providerName === "ollama") {
|
|
591
|
+
const { ollamaBaseURL: ollamaBaseURL2 } = await import("../provider-factory-34MSWJZ3.js");
|
|
592
|
+
const models = await listLocalOllamaModels(ollamaBaseURL2());
|
|
593
|
+
const preferred = DEFAULT_MODELS2[providerName];
|
|
594
|
+
config.model = models.includes(preferred) ? preferred : models[0] ?? preferred;
|
|
595
|
+
} else {
|
|
596
|
+
config.model = DEFAULT_MODELS2[providerName];
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
const provider = await resolveProvider(providerName, {
|
|
600
|
+
apiKey: config.apiKey,
|
|
601
|
+
model: config.model,
|
|
602
|
+
baseURL: config.baseURL
|
|
603
|
+
});
|
|
604
|
+
const thinking = parseThinking(config.thinking);
|
|
605
|
+
const permissionMode = config.permissions ?? "default";
|
|
606
|
+
const agent = new Agent({
|
|
607
|
+
provider,
|
|
608
|
+
sandbox: createCliSandbox(config),
|
|
609
|
+
options: {
|
|
610
|
+
cwd: config.cwd,
|
|
611
|
+
model: config.model,
|
|
612
|
+
systemPrompt: config.systemPrompt,
|
|
613
|
+
permissions: {
|
|
614
|
+
mode: permissionMode
|
|
615
|
+
},
|
|
616
|
+
thinking,
|
|
617
|
+
autoCompact: config.autoCompact ?? true,
|
|
618
|
+
enableSubagents: config.enableSubagents ?? true,
|
|
619
|
+
enableTasks: config.enableTasks ?? false,
|
|
620
|
+
enablePlanMode: config.enablePlanMode ?? true,
|
|
621
|
+
enableWorktrees: config.enableWorktrees ?? false,
|
|
622
|
+
mcpServers: config.mcpServers,
|
|
623
|
+
lsp: config.lsp,
|
|
624
|
+
hooks: config.hooks,
|
|
625
|
+
webSearch: config.webSearch,
|
|
626
|
+
sessionDir: config.sessionDir ?? ".noumen/sessions",
|
|
627
|
+
projectContext: { cwd: config.cwd, homeDir: os2.homedir() },
|
|
628
|
+
costTracking: { enabled: true },
|
|
629
|
+
retry: true
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
if (config.headless) {
|
|
633
|
+
const { runHeadless } = await import("../headless-Q7XHHZIW.js");
|
|
634
|
+
await runHeadless(agent, config);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
await agent.init();
|
|
638
|
+
if (config.prompt) {
|
|
639
|
+
await runOneShot(agent, config);
|
|
640
|
+
} else {
|
|
641
|
+
if (!process.stdin.isTTY) {
|
|
642
|
+
process.stderr.write(
|
|
643
|
+
chalk3.red("Interactive mode requires a TTY. Use -c or pipe input.\n")
|
|
644
|
+
);
|
|
645
|
+
process.exit(1);
|
|
646
|
+
}
|
|
647
|
+
await startRepl(agent, config);
|
|
648
|
+
}
|
|
649
|
+
await agent.close();
|
|
650
|
+
}
|
|
651
|
+
async function runOneShot(agent, config) {
|
|
652
|
+
const { startSpinner: startSpinner2 } = await import("../spinner-OJNR6NFO.js");
|
|
653
|
+
const { isVisibleEvent: isVisibleEvent2 } = await import("../render-GRN4ZSSW.js");
|
|
654
|
+
const thread = agent.createThread();
|
|
655
|
+
const state = createRenderState();
|
|
656
|
+
const runOpts = config.maxTurns ? { maxTurns: config.maxTurns } : void 0;
|
|
657
|
+
const spinner = !config.json && !config.quiet ? startSpinner2("Thinking") : null;
|
|
658
|
+
try {
|
|
659
|
+
for await (const event of thread.run(config.prompt, runOpts)) {
|
|
660
|
+
if (!state.showedActivity && spinner && isVisibleEvent2(event, config)) {
|
|
661
|
+
spinner.stop();
|
|
662
|
+
state.showedActivity = true;
|
|
663
|
+
}
|
|
664
|
+
renderEvent(event, config, state);
|
|
665
|
+
}
|
|
666
|
+
} finally {
|
|
667
|
+
spinner?.stop();
|
|
668
|
+
}
|
|
669
|
+
if (config.quiet && state.accumulatedText) {
|
|
670
|
+
process.stdout.write(state.accumulatedText);
|
|
671
|
+
if (!state.accumulatedText.endsWith("\n")) {
|
|
672
|
+
process.stdout.write("\n");
|
|
673
|
+
}
|
|
674
|
+
} else if (state.accumulatedText && !state.accumulatedText.endsWith("\n")) {
|
|
675
|
+
process.stdout.write("\n");
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
async function listSessions() {
|
|
679
|
+
const cwd = process.cwd();
|
|
680
|
+
const config = loadCliConfig(cwd);
|
|
681
|
+
const merged = mergeConfig(config, { cwd });
|
|
682
|
+
const providerName = merged.provider ?? await detectProvider();
|
|
683
|
+
if (!providerName) {
|
|
684
|
+
process.stderr.write(chalk3.red("No provider configured. Run `noumen init` first.\n"));
|
|
685
|
+
process.exit(1);
|
|
686
|
+
}
|
|
687
|
+
const provider = await resolveProvider(providerName, {
|
|
688
|
+
apiKey: merged.apiKey,
|
|
689
|
+
model: merged.model,
|
|
690
|
+
baseURL: merged.baseURL
|
|
691
|
+
});
|
|
692
|
+
const agent = new Agent({
|
|
693
|
+
provider,
|
|
694
|
+
sandbox: UnsandboxedLocal({ cwd }),
|
|
695
|
+
options: { cwd, sessionDir: merged.sessionDir ?? ".noumen/sessions" }
|
|
696
|
+
});
|
|
697
|
+
const sessions = await agent.listSessions();
|
|
698
|
+
if (sessions.length === 0) {
|
|
699
|
+
process.stdout.write(chalk3.dim("No saved sessions.\n"));
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
process.stdout.write(chalk3.bold("Sessions:\n"));
|
|
703
|
+
for (const s of sessions) {
|
|
704
|
+
const title = s.title ? chalk3.white(` ${s.title}`) : "";
|
|
705
|
+
process.stdout.write(
|
|
706
|
+
` ${chalk3.cyan(s.sessionId.slice(0, 8))} ${chalk3.dim(s.createdAt)} ${chalk3.dim(`${s.messageCount} msgs`)}${title}
|
|
707
|
+
`
|
|
708
|
+
);
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
async function resumeSession(sessionId) {
|
|
712
|
+
const cwd = process.cwd();
|
|
713
|
+
const config = loadCliConfig(cwd);
|
|
714
|
+
const merged = mergeConfig(config, { cwd });
|
|
715
|
+
const providerName = merged.provider ?? await detectProvider();
|
|
716
|
+
if (!providerName) {
|
|
717
|
+
process.stderr.write(chalk3.red("No provider configured. Run `noumen init` first.\n"));
|
|
718
|
+
process.exit(1);
|
|
719
|
+
}
|
|
720
|
+
const provider = await resolveProvider(providerName, {
|
|
721
|
+
apiKey: merged.apiKey,
|
|
722
|
+
model: merged.model,
|
|
723
|
+
baseURL: merged.baseURL
|
|
724
|
+
});
|
|
725
|
+
const thinking = parseThinking(merged.thinking);
|
|
726
|
+
const permissionMode = merged.permissions ?? "default";
|
|
727
|
+
const agent = new Agent({
|
|
728
|
+
provider,
|
|
729
|
+
sandbox: createCliSandbox(merged),
|
|
730
|
+
options: {
|
|
731
|
+
cwd,
|
|
732
|
+
model: merged.model,
|
|
733
|
+
permissions: { mode: permissionMode },
|
|
734
|
+
thinking,
|
|
735
|
+
autoCompact: merged.autoCompact ?? true,
|
|
736
|
+
enableSubagents: merged.enableSubagents ?? true,
|
|
737
|
+
enablePlanMode: merged.enablePlanMode ?? true,
|
|
738
|
+
mcpServers: merged.mcpServers,
|
|
739
|
+
lsp: merged.lsp,
|
|
740
|
+
hooks: merged.hooks,
|
|
741
|
+
sessionDir: merged.sessionDir ?? ".noumen/sessions",
|
|
742
|
+
projectContext: { cwd, homeDir: os2.homedir() },
|
|
743
|
+
costTracking: { enabled: true },
|
|
744
|
+
retry: true
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
await agent.init();
|
|
748
|
+
const sessions = await agent.listSessions();
|
|
749
|
+
const match = sessions.find(
|
|
750
|
+
(s) => s.sessionId === sessionId || s.sessionId.startsWith(sessionId)
|
|
751
|
+
);
|
|
752
|
+
if (!match) {
|
|
753
|
+
process.stderr.write(chalk3.red(`Session not found: ${sessionId}
|
|
754
|
+
`));
|
|
755
|
+
process.exit(1);
|
|
756
|
+
}
|
|
757
|
+
process.stderr.write(
|
|
758
|
+
chalk3.dim(`Resuming session ${match.sessionId.slice(0, 8)}...
|
|
759
|
+
|
|
760
|
+
`)
|
|
761
|
+
);
|
|
762
|
+
const { createInterface: createInterface3 } = await import("readline/promises");
|
|
763
|
+
const rl = createInterface3({ input: process.stdin, output: process.stderr, terminal: true });
|
|
764
|
+
const thread = agent.resumeThread(match.sessionId);
|
|
765
|
+
const { renderEvent: render, createRenderState: makeState } = await import("../render-GRN4ZSSW.js");
|
|
766
|
+
process.stderr.write(chalk3.dim("Session resumed. Type a message to continue.\n\n"));
|
|
767
|
+
try {
|
|
768
|
+
while (true) {
|
|
769
|
+
let input;
|
|
770
|
+
try {
|
|
771
|
+
input = await rl.question(chalk3.blue("> "));
|
|
772
|
+
} catch {
|
|
773
|
+
break;
|
|
774
|
+
}
|
|
775
|
+
if (!input.trim()) continue;
|
|
776
|
+
if (input.trim() === "/quit" || input.trim() === "/exit") break;
|
|
777
|
+
const state = makeState();
|
|
778
|
+
for await (const event of thread.run(input)) {
|
|
779
|
+
render(event, merged, state);
|
|
780
|
+
}
|
|
781
|
+
if (state.accumulatedText && !state.accumulatedText.endsWith("\n")) {
|
|
782
|
+
process.stdout.write("\n");
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
} finally {
|
|
786
|
+
rl.close();
|
|
787
|
+
await agent.close();
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
async function runDoctor() {
|
|
791
|
+
const cwd = process.cwd();
|
|
792
|
+
const config = loadCliConfig(cwd);
|
|
793
|
+
const merged = mergeConfig(config, { cwd });
|
|
794
|
+
const providerName = merged.provider ?? await detectProvider();
|
|
795
|
+
if (!providerName) {
|
|
796
|
+
process.stderr.write(chalk3.red("No provider configured. Run `noumen init` first.\n"));
|
|
797
|
+
process.exit(1);
|
|
798
|
+
}
|
|
799
|
+
const provider = await resolveProvider(providerName, {
|
|
800
|
+
apiKey: merged.apiKey,
|
|
801
|
+
model: merged.model,
|
|
802
|
+
baseURL: merged.baseURL
|
|
803
|
+
});
|
|
804
|
+
const agent = new Agent({
|
|
805
|
+
provider,
|
|
806
|
+
sandbox: createCliSandbox(merged),
|
|
807
|
+
options: {
|
|
808
|
+
cwd,
|
|
809
|
+
model: merged.model,
|
|
810
|
+
mcpServers: merged.mcpServers,
|
|
811
|
+
lsp: merged.lsp,
|
|
812
|
+
sessionDir: merged.sessionDir ?? ".noumen/sessions"
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
await agent.init();
|
|
816
|
+
process.stderr.write(chalk3.bold("\nnoumen doctor\n\n"));
|
|
817
|
+
const result = await agent.diagnose();
|
|
818
|
+
printDiagnoseResult(result);
|
|
819
|
+
await agent.close();
|
|
820
|
+
process.exit(result.overall ? 0 : 1);
|
|
821
|
+
}
|
|
822
|
+
function formatCheckLine(label, check, extra) {
|
|
823
|
+
const icon = check.ok ? chalk3.green("\u2713") : chalk3.red("\u2717");
|
|
824
|
+
const timing = check.latencyMs > 0 ? chalk3.dim(` (${check.latencyMs}ms)`) : "";
|
|
825
|
+
const suffix = extra ? chalk3.dim(` ${extra}`) : "";
|
|
826
|
+
const errMsg = check.error ? chalk3.red(` ${check.error}`) : "";
|
|
827
|
+
const warnMsg = !check.error && check.warning ? chalk3.yellow(` ${check.warning}`) : "";
|
|
828
|
+
return ` ${icon} ${label}${timing}${suffix}${errMsg}${warnMsg}
|
|
829
|
+
`;
|
|
830
|
+
}
|
|
831
|
+
function printDiagnoseResult(r) {
|
|
832
|
+
const modelLabel = r.provider.model ? ` (${r.provider.model})` : "";
|
|
833
|
+
process.stderr.write(formatCheckLine(`Provider${modelLabel}`, r.provider));
|
|
834
|
+
process.stderr.write(formatCheckLine("Sandbox: filesystem", r.sandbox.fs));
|
|
835
|
+
process.stderr.write(formatCheckLine("Sandbox: shell", r.sandbox.computer));
|
|
836
|
+
process.stderr.write(formatCheckLine(
|
|
837
|
+
"Sandbox: OS-level (sandbox-runtime)",
|
|
838
|
+
r.sandboxRuntime,
|
|
839
|
+
r.sandboxRuntime.platform
|
|
840
|
+
));
|
|
841
|
+
for (const [name, check] of Object.entries(r.mcp)) {
|
|
842
|
+
const parts = [];
|
|
843
|
+
if (check.status) parts.push(check.status);
|
|
844
|
+
if (check.toolCount != null) parts.push(`${check.toolCount} tools`);
|
|
845
|
+
const extra = parts.length ? parts.join(", ") : void 0;
|
|
846
|
+
process.stderr.write(formatCheckLine(`MCP: ${name}`, check, extra));
|
|
847
|
+
}
|
|
848
|
+
for (const [name, check] of Object.entries(r.lsp)) {
|
|
849
|
+
const extra = check.state ?? void 0;
|
|
850
|
+
process.stderr.write(formatCheckLine(`LSP: ${name}`, check, extra));
|
|
851
|
+
}
|
|
852
|
+
process.stderr.write("\n");
|
|
853
|
+
if (r.overall) {
|
|
854
|
+
process.stderr.write(` ${chalk3.green("Overall: healthy")}
|
|
855
|
+
|
|
856
|
+
`);
|
|
857
|
+
} else {
|
|
858
|
+
process.stderr.write(` ${chalk3.red("Overall: unhealthy")}
|
|
859
|
+
|
|
860
|
+
`);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
main().catch((err) => {
|
|
864
|
+
process.stderr.write(chalk3.red(`Fatal: ${err.message}
|
|
865
|
+
`));
|
|
866
|
+
process.exit(1);
|
|
867
|
+
});
|
|
868
|
+
//# sourceMappingURL=index.js.map
|