palabre 0.8.1 → 0.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/adapters/index.js +2 -2
- package/dist/adapters/ollama.js +8 -6
- package/dist/agentRegistry.js +6 -0
- package/dist/args.js +1 -0
- package/dist/config.js +8 -0
- package/dist/discovery.js +29 -6
- package/dist/doctor.js +19 -4
- package/dist/index.js +114 -36
- package/dist/messages/common.js +6 -0
- package/dist/messages/doctor.js +2 -2
- package/dist/messages/help.js +6 -0
- package/dist/messages/tui.js +14 -0
- package/dist/ollamaUrl.js +76 -0
- package/dist/orchestrator.js +22 -9
- package/dist/presets.js +25 -4
- package/dist/renderers/tui.js +49 -7
- package/dist/tuiState.js +1 -0
- package/package.json +1 -1
package/dist/adapters/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import { CliAdapter } from "./cli.js";
|
|
|
2
2
|
import { CliPtyAdapter } from "./cli-pty.js";
|
|
3
3
|
import { OllamaAdapter } from "./ollama.js";
|
|
4
4
|
/** Factory qui instancie l'adapter approprié selon `config.type`. Exhaustive : tout `AgentConfig` valide produit un adapter. */
|
|
5
|
-
export function createAgent(name, config) {
|
|
5
|
+
export function createAgent(name, config, runtime = {}) {
|
|
6
6
|
switch (config.type) {
|
|
7
7
|
case "cli":
|
|
8
8
|
return new CliAdapter(name, config);
|
|
9
9
|
case "cli-pty":
|
|
10
10
|
return new CliPtyAdapter(name, config);
|
|
11
11
|
case "ollama":
|
|
12
|
-
return new OllamaAdapter(name, config);
|
|
12
|
+
return new OllamaAdapter(name, config, runtime);
|
|
13
13
|
}
|
|
14
14
|
}
|
package/dist/adapters/ollama.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { AdapterError } from "../errors.js";
|
|
2
2
|
import { createTranslator } from "../i18n.js";
|
|
3
3
|
import { formatAgentPrompt } from "../prompt.js";
|
|
4
|
+
import { resolveOllamaBaseUrl } from "../ollamaUrl.js";
|
|
4
5
|
/**
|
|
5
6
|
* Adapter pour Ollama via l'API HTTP locale (`POST /api/chat`).
|
|
6
7
|
* N'accède jamais au filesystem : ne voit que le prompt et le transcript fournis par l'orchestrateur.
|
|
@@ -9,11 +10,13 @@ import { formatAgentPrompt } from "../prompt.js";
|
|
|
9
10
|
export class OllamaAdapter {
|
|
10
11
|
name;
|
|
11
12
|
config;
|
|
13
|
+
runtime;
|
|
12
14
|
role;
|
|
13
15
|
contract;
|
|
14
|
-
constructor(name, config) {
|
|
16
|
+
constructor(name, config, runtime = {}) {
|
|
15
17
|
this.name = name;
|
|
16
18
|
this.config = config;
|
|
19
|
+
this.runtime = runtime;
|
|
17
20
|
this.role = config.role;
|
|
18
21
|
this.contract = {
|
|
19
22
|
name,
|
|
@@ -38,7 +41,10 @@ export class OllamaAdapter {
|
|
|
38
41
|
if (prompt.signal?.aborted) {
|
|
39
42
|
throw cancelledError(this.name);
|
|
40
43
|
}
|
|
41
|
-
const baseUrl =
|
|
44
|
+
const baseUrl = resolveOllamaBaseUrl({
|
|
45
|
+
cliUrl: this.runtime.ollamaUrl,
|
|
46
|
+
configUrl: this.config.baseUrl
|
|
47
|
+
});
|
|
42
48
|
if (this.config.validateModel !== false) {
|
|
43
49
|
await this.ensureModelAvailable(baseUrl);
|
|
44
50
|
}
|
|
@@ -225,10 +231,6 @@ async function unloadModel(baseUrl, model, signal) {
|
|
|
225
231
|
});
|
|
226
232
|
}
|
|
227
233
|
}
|
|
228
|
-
/** Supprime le slash final de `baseUrl` pour éviter les doubles slashs dans les URLs construites. */
|
|
229
|
-
function normalizeBaseUrl(baseUrl) {
|
|
230
|
-
return baseUrl.replace(/\/$/, "");
|
|
231
|
-
}
|
|
232
234
|
function cancelledError(adapterName) {
|
|
233
235
|
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
234
236
|
}
|
package/dist/agentRegistry.js
CHANGED
|
@@ -10,6 +10,12 @@ const KNOWN_CLI_AGENTS = [
|
|
|
10
10
|
{ configKey: "opencode", commandAliases: ["opencode"], discoveryKey: "opencode" },
|
|
11
11
|
{ configKey: "vibe", commandAliases: ["vibe"], discoveryKey: "vibe" }
|
|
12
12
|
];
|
|
13
|
+
/** Agents retirés conservés uniquement pour lire les anciennes configurations. */
|
|
14
|
+
const RETIRED_AGENT_NAMES = new Set(["gemini"]);
|
|
15
|
+
/** Indique qu'un nom d'agent ne doit plus être proposé ni exposé aux intégrations. */
|
|
16
|
+
export function isRetiredAgentName(name) {
|
|
17
|
+
return RETIRED_AGENT_NAMES.has(name.toLowerCase());
|
|
18
|
+
}
|
|
13
19
|
/** Clé de config de l'agent Ollama local par défaut. */
|
|
14
20
|
export const OLLAMA_AGENT_KEY = "ollama-local";
|
|
15
21
|
/**
|
package/dist/args.js
CHANGED
|
@@ -35,6 +35,7 @@ const FLAG_SPECS = {
|
|
|
35
35
|
language: { arity: "single" },
|
|
36
36
|
"model-a": { arity: "single" },
|
|
37
37
|
"model-b": { arity: "single" },
|
|
38
|
+
"ollama-url": { arity: "single" },
|
|
38
39
|
mode: { arity: "single" },
|
|
39
40
|
"set-ollama-model": { arity: "single" },
|
|
40
41
|
preset: { arity: "single" },
|
package/dist/config.js
CHANGED
|
@@ -270,6 +270,14 @@ export function setOllamaModel(config, model) {
|
|
|
270
270
|
nextModel: agent.model
|
|
271
271
|
};
|
|
272
272
|
}
|
|
273
|
+
/** Met à jour l'adresse persistante de tous les agents Ollama configurés. */
|
|
274
|
+
export function setOllamaBaseUrl(config, baseUrl) {
|
|
275
|
+
const agents = Object.values(config.agents).filter((agent) => agent.type === "ollama");
|
|
276
|
+
for (const agent of agents) {
|
|
277
|
+
agent.baseUrl = baseUrl;
|
|
278
|
+
}
|
|
279
|
+
return agents.length;
|
|
280
|
+
}
|
|
273
281
|
/** Écrit `config` sérialisé en JSON dans `configPath`. Crée le répertoire parent si nécessaire. */
|
|
274
282
|
export async function writeExampleConfig(configPath = DEFAULT_CONFIG_PATH, config = exampleConfig) {
|
|
275
283
|
const resolved = path.resolve(configPath);
|
package/dist/discovery.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { access } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { executableExtensions } from "./exec.js";
|
|
4
|
+
import { resolveOllamaBaseUrl } from "./ollamaUrl.js";
|
|
4
5
|
/**
|
|
5
6
|
* Détecte en parallèle toutes les CLIs supportées et le serveur Ollama local.
|
|
6
7
|
* Sur Windows, tente `claude.exe` avant `claude`.
|
|
7
8
|
* Antigravity est exposé selon les installations sous `agy` ou `antigravity`.
|
|
8
9
|
*/
|
|
9
|
-
export async function discoverLocalTools() {
|
|
10
|
+
export async function discoverLocalTools(options = {}) {
|
|
10
11
|
const [codex, claude, antigravity, opencode, vibe, ollamaCommand] = await Promise.all([
|
|
11
12
|
detectCommand("codex"),
|
|
12
13
|
detectFirstCommand(process.platform === "win32" ? ["claude.exe", "claude"] : ["claude"]),
|
|
@@ -15,17 +16,39 @@ export async function discoverLocalTools() {
|
|
|
15
16
|
detectCommand("vibe"),
|
|
16
17
|
detectCommand("ollama")
|
|
17
18
|
]);
|
|
18
|
-
const
|
|
19
|
+
const configuredTargets = Object.entries(options.ollamaTargets ?? {});
|
|
20
|
+
const targets = configuredTargets.length > 0
|
|
21
|
+
? configuredTargets
|
|
22
|
+
: [["ollama-local", options.ollamaConfigUrl]];
|
|
23
|
+
const resolvedTargets = targets.map(([name, configUrl]) => ({
|
|
24
|
+
name,
|
|
25
|
+
baseUrl: resolveOllamaBaseUrl({
|
|
26
|
+
cliUrl: options.ollamaUrl,
|
|
27
|
+
configUrl
|
|
28
|
+
})
|
|
29
|
+
}));
|
|
30
|
+
const uniqueUrls = resolvedTargets
|
|
31
|
+
.map((target) => target.baseUrl)
|
|
32
|
+
.filter((baseUrl, index, urls) => urls.indexOf(baseUrl) === index);
|
|
33
|
+
const servers = await Promise.all(uniqueUrls.map(async (baseUrl) => [
|
|
34
|
+
baseUrl,
|
|
35
|
+
await detectOllamaServer(baseUrl)
|
|
36
|
+
]));
|
|
37
|
+
const serversByUrl = new Map(servers);
|
|
38
|
+
const ollamaAgents = Object.fromEntries(resolvedTargets.map(({ name, baseUrl }) => [name, {
|
|
39
|
+
...serversByUrl.get(baseUrl),
|
|
40
|
+
commandAvailable: ollamaCommand.available
|
|
41
|
+
}]));
|
|
42
|
+
const primaryName = ollamaAgents["ollama-local"] ? "ollama-local" : resolvedTargets[0].name;
|
|
43
|
+
const ollamaServer = ollamaAgents[primaryName];
|
|
19
44
|
return {
|
|
20
45
|
codex,
|
|
21
46
|
claude,
|
|
22
47
|
antigravity,
|
|
23
48
|
opencode,
|
|
24
49
|
vibe,
|
|
25
|
-
ollama:
|
|
26
|
-
|
|
27
|
-
commandAvailable: ollamaCommand.available
|
|
28
|
-
}
|
|
50
|
+
ollama: ollamaServer,
|
|
51
|
+
ollamaAgents
|
|
29
52
|
};
|
|
30
53
|
}
|
|
31
54
|
async function detectFirstCommand(commands) {
|
package/dist/doctor.js
CHANGED
|
@@ -5,6 +5,7 @@ import { detectedAgentNames, detectionForCommand } from "./agentRegistry.js";
|
|
|
5
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
6
6
|
import { createTranslator, resolveLanguage } from "./i18n.js";
|
|
7
7
|
import { DEFAULT_TURNS, MAX_TURNS } from "./limits.js";
|
|
8
|
+
import { configuredOllamaTargets, normalizeOllamaBaseUrl } from "./ollamaUrl.js";
|
|
8
9
|
import { compareSemver, getLatestPackageVersion, getPackageVersion } from "./version.js";
|
|
9
10
|
/**
|
|
10
11
|
* Exécute le diagnostic complet : config, outils locaux et agents.
|
|
@@ -39,7 +40,11 @@ export async function runDoctor(explicitConfigPath, plain = false, explicitLangu
|
|
|
39
40
|
lines.push(ok(t.doctor.configReadable));
|
|
40
41
|
lines.push(ok(t.doctor.interfaceLanguage(language)));
|
|
41
42
|
await inspectConfig(config, lines, t);
|
|
42
|
-
const
|
|
43
|
+
const ollamaTargets = Object.fromEntries(Object.entries(configuredOllamaTargets(config))
|
|
44
|
+
.map(([name, value]) => [name, value && isValidOllamaBaseUrl(value) ? value : undefined]));
|
|
45
|
+
const discovery = await discoverLocalTools({
|
|
46
|
+
ollamaTargets
|
|
47
|
+
});
|
|
43
48
|
lines.push(info(t.doctor.localTools, "tools"));
|
|
44
49
|
lines.push(formatCommand("Codex CLI", discovery.codex.available, discovery.codex.command, discovery.codex.path, t));
|
|
45
50
|
lines.push(formatCommand("Claude CLI", discovery.claude.available, discovery.claude.command, discovery.claude.path, t));
|
|
@@ -204,13 +209,22 @@ function inspectAgentShape(name, agent, lines, t) {
|
|
|
204
209
|
if (!agent.model || !agent.model.trim()) {
|
|
205
210
|
lines.push(error(t.doctor.ollamaModelMissing(name)));
|
|
206
211
|
}
|
|
207
|
-
if (agent.baseUrl &&
|
|
212
|
+
if (agent.baseUrl && !isValidOllamaBaseUrl(agent.baseUrl)) {
|
|
208
213
|
lines.push(error(t.doctor.ollamaBaseUrlInvalid(name, agent.baseUrl)));
|
|
209
214
|
}
|
|
210
215
|
if (agent.timeoutMs !== undefined && (!Number.isFinite(agent.timeoutMs) || agent.timeoutMs <= 0)) {
|
|
211
216
|
lines.push(error(t.doctor.positiveTimeout(name, "timeoutMs")));
|
|
212
217
|
}
|
|
213
218
|
}
|
|
219
|
+
function isValidOllamaBaseUrl(value) {
|
|
220
|
+
try {
|
|
221
|
+
normalizeOllamaBaseUrl(value);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return false;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
214
228
|
function inspectCliAgent(name, agent, discovery, lines, t) {
|
|
215
229
|
const known = detectionForCommand(agent.command, discovery);
|
|
216
230
|
const prefix = `${name} [cli:${agent.role}] command=${agent.command}`;
|
|
@@ -224,7 +238,8 @@ function inspectCliAgent(name, agent, discovery, lines, t) {
|
|
|
224
238
|
}
|
|
225
239
|
function inspectOllamaAgent(name, agent, discovery, lines, t) {
|
|
226
240
|
const prefix = `${name} [ollama:${agent.role}] model=${agent.model}`;
|
|
227
|
-
|
|
241
|
+
const ollama = discovery.ollamaAgents?.[name] ?? discovery.ollama;
|
|
242
|
+
if (!ollama.available) {
|
|
228
243
|
lines.push(warn(t.doctor.ollamaNotVerifiable(prefix)));
|
|
229
244
|
return;
|
|
230
245
|
}
|
|
@@ -232,7 +247,7 @@ function inspectOllamaAgent(name, agent, discovery, lines, t) {
|
|
|
232
247
|
lines.push(info(t.doctor.ollamaValidateFalse(prefix)));
|
|
233
248
|
return;
|
|
234
249
|
}
|
|
235
|
-
const installed =
|
|
250
|
+
const installed = ollama.models.includes(agent.model);
|
|
236
251
|
lines.push(installed
|
|
237
252
|
? ok(t.doctor.ollamaInstalled(prefix))
|
|
238
253
|
: warn(t.doctor.ollamaMissing(prefix, agent.model)));
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaModel, syncDetectedAgentsDetailed, syncOllamaModel, writeExampleConfig } from "./config.js";
|
|
2
|
+
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaBaseUrl, setOllamaModel, syncDetectedAgentsDetailed, syncOllamaModel, writeExampleConfig } from "./config.js";
|
|
3
3
|
import { loadProjectInputs } from "./context.js";
|
|
4
4
|
import { buildContextScan } from "./contextScan.js";
|
|
5
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
@@ -10,19 +10,20 @@ import { createTranslator, DEFAULT_LANGUAGE, parseLanguage, resolveLanguage } fr
|
|
|
10
10
|
import { DEFAULT_TURNS, parseTurnsFlag, turnsOrDefault, validateTurns } from "./limits.js";
|
|
11
11
|
import { formatAgentPrompt } from "./prompt.js";
|
|
12
12
|
import { runNewWizard } from "./new.js";
|
|
13
|
-
import { listPresetNames, listPresetsWithAvailability, resolvePreset } from "./presets.js";
|
|
13
|
+
import { listAgentsWithAvailability, listPresetNames, listPresetsWithAvailability, resolvePreset } from "./presets.js";
|
|
14
14
|
import { listHistoryEntries } from "./history.js";
|
|
15
15
|
import { createConsoleRenderer } from "./renderers/console.js";
|
|
16
16
|
import { createNdjsonRenderer } from "./renderers/ndjson.js";
|
|
17
|
-
import { createTuiRenderer, promptTuiAgentsWizard, promptTuiConfigCommand, promptTuiHomeTopic, promptTuiRolesWizard, renderTuiConfig, renderTuiHelp, renderTuiHistory, renderTuiHome } from "./renderers/tui.js";
|
|
17
|
+
import { createTuiRenderer, promptTuiAgentsWizard, promptTuiConfigCommand, promptTuiHomeTopic, promptTuiRolesWizard, renderTuiConfig, renderTuiHelp, renderTuiHistory, renderTuiHome, renderTuiUpdate } from "./renderers/tui.js";
|
|
18
18
|
import { MAX_ASK_AGENTS, runAsk, runDebate } from "./orchestrator.js";
|
|
19
19
|
import { writeDebateMarkdown } from "./output.js";
|
|
20
20
|
import { applySourceUpdate, formatUpdateInstructions, getUpdateInfo } from "./update.js";
|
|
21
21
|
import { createSessionContext } from "./session.js";
|
|
22
22
|
import { getStringListFlag, parseArgs } from "./args.js";
|
|
23
23
|
import { askAgentSeedsForMode, clearTuiRunOverrides } from "./tuiState.js";
|
|
24
|
-
import { detectedAgentNames, detectionForCommand } from "./agentRegistry.js";
|
|
25
|
-
import {
|
|
24
|
+
import { detectedAgentNames, detectionForCommand, isRetiredAgentName } from "./agentRegistry.js";
|
|
25
|
+
import { configuredOllamaTargets, DEFAULT_OLLAMA_BASE_URL, normalizeOllamaBaseUrl, OllamaUrlError, resolveOllamaBaseUrl } from "./ollamaUrl.js";
|
|
26
|
+
import { compareSemver, getLatestPackageVersion, getPackageVersion } from "./version.js";
|
|
26
27
|
/** Point d'entrée principal du CLI Palabre. Dispatche vers la commande appropriée selon les arguments. */
|
|
27
28
|
async function main() {
|
|
28
29
|
const rawArgs = process.argv.slice(2);
|
|
@@ -88,7 +89,9 @@ async function main() {
|
|
|
88
89
|
console.log(startupMessages.init.configExists(initConfigPath));
|
|
89
90
|
return;
|
|
90
91
|
}
|
|
91
|
-
const discovery = await discoverLocalTools(
|
|
92
|
+
const discovery = await discoverLocalTools({
|
|
93
|
+
ollamaUrl: optionalString(parsed.flags["ollama-url"])
|
|
94
|
+
});
|
|
92
95
|
const config = createConfigFromDiscovery(discovery);
|
|
93
96
|
config.language = resolveLanguage({
|
|
94
97
|
explicitLanguage: optionalString(parsed.flags.language),
|
|
@@ -104,7 +107,9 @@ async function main() {
|
|
|
104
107
|
let config;
|
|
105
108
|
let tuiNotice;
|
|
106
109
|
if (!(await configExists(configPath))) {
|
|
107
|
-
config = createConfigFromDiscovery(await discoverLocalTools(
|
|
110
|
+
config = createConfigFromDiscovery(await discoverLocalTools({
|
|
111
|
+
ollamaUrl: optionalString(parsed.flags["ollama-url"])
|
|
112
|
+
}));
|
|
108
113
|
config.language = resolveLanguage({
|
|
109
114
|
explicitLanguage: optionalString(parsed.flags.language),
|
|
110
115
|
configLanguage: config.language
|
|
@@ -131,6 +136,7 @@ async function main() {
|
|
|
131
136
|
let resetTuiRunOverridesOnNextTopic = false;
|
|
132
137
|
let tuiMode = config.defaults?.mode ?? "debate";
|
|
133
138
|
let tuiVersion = "";
|
|
139
|
+
let tuiLatestVersion;
|
|
134
140
|
const handleTuiHomeInput = async (tuiInput) => {
|
|
135
141
|
if (!tuiInput) {
|
|
136
142
|
return "quit";
|
|
@@ -145,6 +151,12 @@ async function main() {
|
|
|
145
151
|
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
146
152
|
return handleTuiHomeInput(nextInput);
|
|
147
153
|
}
|
|
154
|
+
if (tuiInput.kind === "update") {
|
|
155
|
+
const info = await getUpdateInfo(tuiVersion);
|
|
156
|
+
renderTuiUpdate(formatUpdateInstructions(info, messages), messages);
|
|
157
|
+
const nextInput = await promptTuiHomeTopic(tuiMode, messages);
|
|
158
|
+
return handleTuiHomeInput(nextInput);
|
|
159
|
+
}
|
|
148
160
|
if (tuiInput.kind === "home") {
|
|
149
161
|
return "continue";
|
|
150
162
|
}
|
|
@@ -200,14 +212,21 @@ async function main() {
|
|
|
200
212
|
return "run";
|
|
201
213
|
};
|
|
202
214
|
if (shouldOpenTuiHome(parsed)) {
|
|
203
|
-
const syncResult = await
|
|
215
|
+
const [syncResult, currentVersion, latestVersion] = await Promise.all([
|
|
216
|
+
syncInteractiveDetectedAgents(configPath, config),
|
|
217
|
+
getPackageVersion(),
|
|
218
|
+
getLatestPackageVersion()
|
|
219
|
+
]);
|
|
204
220
|
if (!tuiNotice && syncResult.addedAgents.length > 0) {
|
|
205
221
|
tuiNotice = messages.config.syncAdded(configPath, syncResult.addedAgents.join(", "));
|
|
206
222
|
}
|
|
207
223
|
stayInTuiAfterSession = true;
|
|
208
|
-
tuiVersion =
|
|
224
|
+
tuiVersion = currentVersion;
|
|
225
|
+
tuiLatestVersion = latestVersion && compareSemver(currentVersion, latestVersion) < 0
|
|
226
|
+
? latestVersion
|
|
227
|
+
: undefined;
|
|
209
228
|
for (;;) {
|
|
210
|
-
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion });
|
|
229
|
+
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion, latestVersion: tuiLatestVersion });
|
|
211
230
|
const tuiInput = await promptTuiHomeTopic(tuiMode, messages, { notice: tuiNotice });
|
|
212
231
|
tuiNotice = undefined;
|
|
213
232
|
const action = await handleTuiHomeInput(tuiInput);
|
|
@@ -282,6 +301,9 @@ async function main() {
|
|
|
282
301
|
files: context.files,
|
|
283
302
|
modelA: optionalString(parsed.flags["model-a"]),
|
|
284
303
|
modelB: optionalString(parsed.flags["model-b"]),
|
|
304
|
+
ollamaUrl: optionalString(parsed.flags["ollama-url"])
|
|
305
|
+
? normalizeOllamaBaseUrl(optionalString(parsed.flags["ollama-url"]))
|
|
306
|
+
: undefined,
|
|
285
307
|
pullModels: Boolean(parsed.flags["pull-models"]),
|
|
286
308
|
summaryAgent: resolveSummaryAgentOption(parsed.flags["summary-agent"], config.defaults, mode),
|
|
287
309
|
summaryModel: optionalString(parsed.flags["summary-model"]),
|
|
@@ -318,7 +340,7 @@ async function main() {
|
|
|
318
340
|
if (action === "quit")
|
|
319
341
|
return;
|
|
320
342
|
if (action === "continue") {
|
|
321
|
-
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion });
|
|
343
|
+
renderTuiHome(config, configPath, messages, { mode: tuiMode, version: tuiVersion, latestVersion: tuiLatestVersion });
|
|
322
344
|
continue;
|
|
323
345
|
}
|
|
324
346
|
if (action === "retry") {
|
|
@@ -358,7 +380,21 @@ async function runAgentsCommand(flags) {
|
|
|
358
380
|
configLanguage: config.language
|
|
359
381
|
});
|
|
360
382
|
const messages = createTranslator(language);
|
|
361
|
-
const discovery = await
|
|
383
|
+
const discovery = await discoverLocalToolsForConfig(config, optionalString(flags["ollama-url"]));
|
|
384
|
+
if (flags.json) {
|
|
385
|
+
const fallbackAskAgents = [config.defaults?.agentA, config.defaults?.agentB]
|
|
386
|
+
.filter((name) => typeof name === "string" && !isRetiredAgentName(name));
|
|
387
|
+
process.stdout.write(JSON.stringify({
|
|
388
|
+
v: 1,
|
|
389
|
+
agents: listAgentsWithAvailability(config, discovery, messages),
|
|
390
|
+
defaults: {
|
|
391
|
+
askAgents: config.defaults?.askAgents?.length
|
|
392
|
+
? config.defaults.askAgents.filter((name) => !isRetiredAgentName(name))
|
|
393
|
+
: fallbackAskAgents
|
|
394
|
+
}
|
|
395
|
+
}) + "\n");
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
362
398
|
printAgents(configPath, config, discovery, messages);
|
|
363
399
|
}
|
|
364
400
|
/**
|
|
@@ -394,7 +430,7 @@ async function runConfigCommand(flags) {
|
|
|
394
430
|
return;
|
|
395
431
|
}
|
|
396
432
|
if (flags["sync-agents"]) {
|
|
397
|
-
const discovery = await
|
|
433
|
+
const discovery = await discoverLocalToolsForConfig(config, optionalString(flags["ollama-url"]));
|
|
398
434
|
const result = syncDetectedAgentsDetailed(config, discovery);
|
|
399
435
|
if (!result.changed) {
|
|
400
436
|
console.log(messages.config.syncNoMissing(configPath));
|
|
@@ -639,6 +675,16 @@ async function runTuiConfigLoop(configPath, config, messages, initialMode) {
|
|
|
639
675
|
}
|
|
640
676
|
continue;
|
|
641
677
|
}
|
|
678
|
+
if (input.kind === "ollama-url") {
|
|
679
|
+
try {
|
|
680
|
+
notice = await setTuiOllamaUrl(configPath, config, input.url, currentMessages);
|
|
681
|
+
changedRunDefaults = true;
|
|
682
|
+
}
|
|
683
|
+
catch (error) {
|
|
684
|
+
notice = formatRuntimeError(error, currentMessages);
|
|
685
|
+
}
|
|
686
|
+
continue;
|
|
687
|
+
}
|
|
642
688
|
if (input.kind === "ollama-model") {
|
|
643
689
|
try {
|
|
644
690
|
notice = await setTuiOllamaModel(configPath, config, input.model, currentMessages);
|
|
@@ -661,8 +707,23 @@ async function runTuiConfigLoop(configPath, config, messages, initialMode) {
|
|
|
661
707
|
}
|
|
662
708
|
}
|
|
663
709
|
}
|
|
710
|
+
async function setTuiOllamaUrl(configPath, config, value, messages) {
|
|
711
|
+
if (!Object.values(config.agents).some((agent) => agent.type === "ollama")) {
|
|
712
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
713
|
+
}
|
|
714
|
+
const normalized = isDefaultOllamaUrl(value)
|
|
715
|
+
? DEFAULT_OLLAMA_BASE_URL
|
|
716
|
+
: normalizeOllamaBaseUrl(value);
|
|
717
|
+
const effective = resolveOllamaBaseUrl({ configUrl: normalized });
|
|
718
|
+
setOllamaBaseUrl(config, normalized);
|
|
719
|
+
await writeExampleConfig(configPath, config);
|
|
720
|
+
return messages.tui.ollamaUrlUpdated(normalized, effective);
|
|
721
|
+
}
|
|
722
|
+
function isDefaultOllamaUrl(value) {
|
|
723
|
+
return ["default", "defaut", "défaut", "local", "localhost"].includes(value.trim().toLowerCase());
|
|
724
|
+
}
|
|
664
725
|
async function formatTuiOllamaInfo(config, messages) {
|
|
665
|
-
const discovery = await
|
|
726
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
666
727
|
const agent = config.agents["ollama-local"];
|
|
667
728
|
if (agent?.type !== "ollama") {
|
|
668
729
|
throw new Error(messages.config.ollamaModelNoAgent);
|
|
@@ -681,7 +742,7 @@ async function setTuiOllamaModel(configPath, config, model, messages) {
|
|
|
681
742
|
if (!trimmed) {
|
|
682
743
|
throw new Error(messages.tui.ollamaModelUsage);
|
|
683
744
|
}
|
|
684
|
-
const discovery = await
|
|
745
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
685
746
|
const agent = config.agents["ollama-local"];
|
|
686
747
|
if (agent?.type !== "ollama") {
|
|
687
748
|
throw new Error(messages.config.ollamaModelNoAgent);
|
|
@@ -696,7 +757,7 @@ async function setTuiOllamaModel(configPath, config, model, messages) {
|
|
|
696
757
|
: messages.config.ollamaModelNoChange(configPath, agent.model);
|
|
697
758
|
}
|
|
698
759
|
async function syncTuiOllamaModel(configPath, config, messages) {
|
|
699
|
-
const discovery = await
|
|
760
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
700
761
|
const agent = config.agents["ollama-local"];
|
|
701
762
|
if (agent?.type !== "ollama") {
|
|
702
763
|
throw new Error(messages.config.ollamaModelNoAgent);
|
|
@@ -712,7 +773,7 @@ async function syncTuiOllamaModel(configPath, config, messages) {
|
|
|
712
773
|
return messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel);
|
|
713
774
|
}
|
|
714
775
|
async function syncInteractiveDetectedAgents(configPath, config) {
|
|
715
|
-
const discovery = await
|
|
776
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
716
777
|
const result = syncDetectedAgentsDetailed(config, discovery);
|
|
717
778
|
if (result.changed) {
|
|
718
779
|
await writeExampleConfig(configPath, config);
|
|
@@ -833,7 +894,7 @@ function isAgentRole(value) {
|
|
|
833
894
|
}
|
|
834
895
|
const VALID_AGENT_ROLES = ["implementer", "reviewer", "architect", "scout", "critic", "summarizer"];
|
|
835
896
|
async function runOllamaModelsCommand(config, json) {
|
|
836
|
-
const discovery = await
|
|
897
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
837
898
|
const agent = config.agents["ollama-local"];
|
|
838
899
|
const currentModel = agent?.type === "ollama" ? agent.model : null;
|
|
839
900
|
const payload = {
|
|
@@ -858,7 +919,7 @@ async function runSetOllamaModelCommand(configPath, config, model, messages) {
|
|
|
858
919
|
if (!trimmed) {
|
|
859
920
|
throw new Error(messages.common.optionRequiresValue("--set-ollama-model"));
|
|
860
921
|
}
|
|
861
|
-
const discovery = await
|
|
922
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
862
923
|
const agent = config.agents["ollama-local"];
|
|
863
924
|
if (agent?.type !== "ollama") {
|
|
864
925
|
throw new Error(messages.config.ollamaModelNoAgent);
|
|
@@ -873,7 +934,7 @@ async function runSetOllamaModelCommand(configPath, config, model, messages) {
|
|
|
873
934
|
: messages.config.ollamaModelNoChange(configPath, agent.model));
|
|
874
935
|
}
|
|
875
936
|
async function runSyncOllamaModelCommand(configPath, config, messages) {
|
|
876
|
-
const discovery = await
|
|
937
|
+
const discovery = await discoverLocalToolsForConfig(config);
|
|
877
938
|
const agent = config.agents["ollama-local"];
|
|
878
939
|
if (agent?.type !== "ollama") {
|
|
879
940
|
throw new Error(messages.config.ollamaModelNoAgent);
|
|
@@ -1110,28 +1171,32 @@ function shouldOpenTuiHome(parsed) {
|
|
|
1110
1171
|
&& parsed.flags.plain !== true
|
|
1111
1172
|
&& parsed.flags.terminal !== true;
|
|
1112
1173
|
}
|
|
1174
|
+
/** Lance la discovery avec la même adresse Ollama effective que la config et les overrides globaux. */
|
|
1175
|
+
async function discoverLocalToolsForConfig(config, ollamaUrl) {
|
|
1176
|
+
return discoverLocalTools({
|
|
1177
|
+
ollamaUrl,
|
|
1178
|
+
ollamaTargets: configuredOllamaTargets(config)
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1113
1181
|
/**
|
|
1114
|
-
* Exécute la commande `palabre presets
|
|
1115
|
-
*
|
|
1116
|
-
* Sortie humaine par défaut (liste alignée), ou JSON avec `--json` pour les
|
|
1117
|
-
* intégrations (extension VS Code, scripts shell). Le schéma JSON est versionné
|
|
1118
|
-
* via le champ `v` au cas où on enrichirait plus tard (ex : description par
|
|
1119
|
-
* preset, tags premium/local).
|
|
1120
|
-
*
|
|
1121
|
-
* @param flags - Flags parsés depuis la ligne de commande.
|
|
1182
|
+
* Exécute la commande `palabre presets` en sortie humaine ou JSON versionné.
|
|
1122
1183
|
*/
|
|
1123
1184
|
async function runPresetsCommand(flags) {
|
|
1124
|
-
const discovery = await discoverLocalTools();
|
|
1125
1185
|
const configPath = optionalString(flags.config) ?? await resolveDefaultConfigPath();
|
|
1186
|
+
const ollamaUrl = optionalString(flags["ollama-url"]);
|
|
1126
1187
|
const config = await configExists(configPath)
|
|
1127
1188
|
? await loadConfig(configPath)
|
|
1128
|
-
:
|
|
1189
|
+
: undefined;
|
|
1190
|
+
const discovery = config
|
|
1191
|
+
? await discoverLocalToolsForConfig(config, ollamaUrl)
|
|
1192
|
+
: await discoverLocalTools({ ollamaUrl });
|
|
1193
|
+
const resolvedConfig = config ?? createConfigFromDiscovery(discovery);
|
|
1129
1194
|
const language = resolveLanguage({
|
|
1130
1195
|
explicitLanguage: optionalString(flags.language),
|
|
1131
|
-
configLanguage:
|
|
1196
|
+
configLanguage: resolvedConfig.language
|
|
1132
1197
|
});
|
|
1133
1198
|
const messages = createTranslator(language);
|
|
1134
|
-
const presets = listPresetsWithAvailability(
|
|
1199
|
+
const presets = listPresetsWithAvailability(resolvedConfig, discovery, messages);
|
|
1135
1200
|
if (flags.json) {
|
|
1136
1201
|
process.stdout.write(JSON.stringify({ v: 1, presets }) + "\n");
|
|
1137
1202
|
return;
|
|
@@ -1221,7 +1286,9 @@ function printContextWarnings(warnings, messages) {
|
|
|
1221
1286
|
* @param discovery - Résultat de la découverte locale des outils.
|
|
1222
1287
|
*/
|
|
1223
1288
|
function printAgents(configPath, config, discovery, messages) {
|
|
1224
|
-
const entries = Object.entries(config.agents)
|
|
1289
|
+
const entries = Object.entries(config.agents)
|
|
1290
|
+
.filter(([name]) => !isRetiredAgentName(name))
|
|
1291
|
+
.sort(([left], [right]) => left.localeCompare(right));
|
|
1225
1292
|
console.log(messages.agents.config(configPath));
|
|
1226
1293
|
console.log("");
|
|
1227
1294
|
console.log(messages.agents.title);
|
|
@@ -1383,12 +1450,23 @@ async function resolveCommandMessages(flags) {
|
|
|
1383
1450
|
}
|
|
1384
1451
|
return createTranslator(resolveLanguage({ explicitLanguage, configLanguage }));
|
|
1385
1452
|
}
|
|
1453
|
+
function formatRuntimeError(error, messages) {
|
|
1454
|
+
if (error instanceof AdapterError) {
|
|
1455
|
+
return formatAdapterError(error, messages);
|
|
1456
|
+
}
|
|
1457
|
+
if (error instanceof OllamaUrlError) {
|
|
1458
|
+
if (error.kind === "empty")
|
|
1459
|
+
return messages.common.ollamaUrlEmpty;
|
|
1460
|
+
if (error.kind === "protocol")
|
|
1461
|
+
return messages.common.ollamaUrlProtocol(error.protocol ?? "");
|
|
1462
|
+
return messages.common.ollamaUrlInvalid(error.value);
|
|
1463
|
+
}
|
|
1464
|
+
return error instanceof Error ? error.message : String(error);
|
|
1465
|
+
}
|
|
1386
1466
|
main().catch((error) => {
|
|
1387
1467
|
const language = safeStartupLanguage(process.argv.slice(2));
|
|
1388
1468
|
const messages = createTranslator(language);
|
|
1389
|
-
const message = error
|
|
1390
|
-
? formatAdapterError(error, messages)
|
|
1391
|
-
: error instanceof Error ? error.message : String(error);
|
|
1469
|
+
const message = formatRuntimeError(error, messages);
|
|
1392
1470
|
console.error(`${messages.common.errorPrefix}: ${message}`);
|
|
1393
1471
|
process.exitCode = 1;
|
|
1394
1472
|
});
|
package/dist/messages/common.js
CHANGED
|
@@ -15,6 +15,9 @@ export const commonMessages = {
|
|
|
15
15
|
configInvalidShape: (configPath) => `Config invalide: ${configPath} ne contient pas un objet JSON. Relance palabre init ou corrige le fichier.`,
|
|
16
16
|
configMissingAgents: (configPath) => `Config invalide: ${configPath} ne déclare pas de bloc "agents". Relance palabre init ou ajoute au moins un agent.`,
|
|
17
17
|
configEmptyAgents: (configPath) => `Config invalide: ${configPath} ne déclare aucun agent. Ajoute au moins un agent ou relance palabre init.`,
|
|
18
|
+
ollamaUrlEmpty: "L'adresse Ollama ne peut pas être vide.",
|
|
19
|
+
ollamaUrlInvalid: (value) => `Adresse Ollama invalide: ${value}.`,
|
|
20
|
+
ollamaUrlProtocol: (protocol) => `Protocole Ollama invalide: ${protocol}. Utilise http: ou https:.`,
|
|
18
21
|
errorPrefix: "Erreur"
|
|
19
22
|
},
|
|
20
23
|
en: {
|
|
@@ -33,6 +36,9 @@ export const commonMessages = {
|
|
|
33
36
|
configInvalidShape: (configPath) => `Invalid config: ${configPath} does not contain a JSON object. Run palabre init or fix the file.`,
|
|
34
37
|
configMissingAgents: (configPath) => `Invalid config: ${configPath} has no "agents" block. Run palabre init or add at least one agent.`,
|
|
35
38
|
configEmptyAgents: (configPath) => `Invalid config: ${configPath} declares no agent. Add at least one agent or run palabre init.`,
|
|
39
|
+
ollamaUrlEmpty: "The Ollama address cannot be empty.",
|
|
40
|
+
ollamaUrlInvalid: (value) => `Invalid Ollama address: ${value}.`,
|
|
41
|
+
ollamaUrlProtocol: (protocol) => `Invalid Ollama protocol: ${protocol}. Use http: or https:.`,
|
|
36
42
|
errorPrefix: "Error"
|
|
37
43
|
}
|
|
38
44
|
};
|
package/dist/messages/doctor.js
CHANGED
|
@@ -41,7 +41,7 @@ export const doctorMessages = {
|
|
|
41
41
|
promptModeInvalid: (name, value) => `${name}: promptMode invalide (${value}). Valeurs attendues: stdin ou argument.`,
|
|
42
42
|
positiveTimeout: (name, field) => `${name}: ${field} doit être un nombre positif.`,
|
|
43
43
|
ollamaModelMissing: (name) => `${name}: modèle Ollama absent.`,
|
|
44
|
-
ollamaBaseUrlInvalid: (name, value) => `${name}: baseUrl Ollama invalide (${value}). Attendu:
|
|
44
|
+
ollamaBaseUrlInvalid: (name, value) => `${name}: baseUrl Ollama invalide (${value}). Attendu: une adresse HTTP(S), avec ou sans schema.`,
|
|
45
45
|
customCommand: (prefix) => `${prefix} (commande custom non vérifiée par doctor)`,
|
|
46
46
|
cliDetected: (prefix, path) => `${prefix} détectée (${path})`,
|
|
47
47
|
cliMissing: (prefix) => `${prefix} non détectée dans PATH. Action: installe/authentifie la CLI ou corrige command dans la config.`,
|
|
@@ -107,7 +107,7 @@ export const doctorMessages = {
|
|
|
107
107
|
promptModeInvalid: (name, value) => `${name}: invalid promptMode (${value}). Expected values: stdin or argument.`,
|
|
108
108
|
positiveTimeout: (name, field) => `${name}: ${field} must be a positive number.`,
|
|
109
109
|
ollamaModelMissing: (name) => `${name}: missing Ollama model.`,
|
|
110
|
-
ollamaBaseUrlInvalid: (name, value) => `${name}: invalid Ollama baseUrl (${value}). Expected:
|
|
110
|
+
ollamaBaseUrlInvalid: (name, value) => `${name}: invalid Ollama baseUrl (${value}). Expected: an HTTP(S) address, with or without a scheme.`,
|
|
111
111
|
customCommand: (prefix) => `${prefix} (custom command not checked by doctor)`,
|
|
112
112
|
cliDetected: (prefix, path) => `${prefix} detected (${path})`,
|
|
113
113
|
cliMissing: (prefix) => `${prefix} not detected in PATH. Action: install/authenticate the CLI or fix command in the config.`,
|
package/dist/messages/help.js
CHANGED
|
@@ -22,6 +22,7 @@ Usage:
|
|
|
22
22
|
Flags:
|
|
23
23
|
--config <path> chemin de config explicite
|
|
24
24
|
--language <fr|en> force la langue
|
|
25
|
+
--json sortie JSON pour les integrations
|
|
25
26
|
`,
|
|
26
27
|
presets: `
|
|
27
28
|
Liste les presets de paires d'agents.
|
|
@@ -129,6 +130,7 @@ Flags:
|
|
|
129
130
|
--preset <name> preset d'agents
|
|
130
131
|
--agent-a <name> premier agent
|
|
131
132
|
--agent-b <name> second agent
|
|
133
|
+
--ollama-url <url> surcharge l'adresse Ollama pour cette session
|
|
132
134
|
--tui force l'interface TUI
|
|
133
135
|
--terminal force le rendu terminal brut
|
|
134
136
|
--renderer <kind> auto, pretty, plain, tui ou ndjson
|
|
@@ -144,6 +146,7 @@ Usage:
|
|
|
144
146
|
Flags:
|
|
145
147
|
--agents <names...> agents qui repondent, 4 maximum
|
|
146
148
|
--summary-agent <n> agent de synthese pour ce lancement
|
|
149
|
+
--ollama-url <url> surcharge l'adresse Ollama pour cette session
|
|
147
150
|
--tui force l'interface TUI
|
|
148
151
|
--terminal force le rendu terminal brut
|
|
149
152
|
--renderer <kind> auto, pretty, plain, tui ou ndjson
|
|
@@ -174,6 +177,7 @@ Usage:
|
|
|
174
177
|
Flags:
|
|
175
178
|
--config <path> explicit config path
|
|
176
179
|
--language <fr|en> forces the language
|
|
180
|
+
--json JSON output for integrations
|
|
177
181
|
`,
|
|
178
182
|
presets: `
|
|
179
183
|
Lists agent-pair presets.
|
|
@@ -281,6 +285,7 @@ Flags:
|
|
|
281
285
|
--preset <name> agent preset
|
|
282
286
|
--agent-a <name> first agent
|
|
283
287
|
--agent-b <name> second agent
|
|
288
|
+
--ollama-url <url> overrides the Ollama address for this session
|
|
284
289
|
--tui forces the TUI interface
|
|
285
290
|
--terminal forces raw terminal rendering
|
|
286
291
|
--renderer <kind> auto, pretty, plain, tui, or ndjson
|
|
@@ -296,6 +301,7 @@ Usage:
|
|
|
296
301
|
Flags:
|
|
297
302
|
--agents <names...> responding agents, 4 maximum
|
|
298
303
|
--summary-agent <n> summary agent for this run
|
|
304
|
+
--ollama-url <url> overrides the Ollama address for this session
|
|
299
305
|
--tui forces the TUI interface
|
|
300
306
|
--terminal forces raw terminal rendering
|
|
301
307
|
--renderer <kind> auto, pretty, plain, tui, or ndjson
|
package/dist/messages/tui.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
export const tuiMessages = {
|
|
2
2
|
fr: {
|
|
3
3
|
tagline: "Orchestrez des conversations entre agents IA",
|
|
4
|
+
updateAvailable: (current, latest) => `Mise a jour disponible: ${current} -> ${latest}. Utilise /update.`,
|
|
4
5
|
modeLabel: (mode) => mode === "ask" ? "Ask" : "Debat",
|
|
5
6
|
modeValue: (mode) => mode === "ask" ? "Ask" : "Debat",
|
|
6
7
|
noValue: "non definis",
|
|
@@ -8,6 +9,8 @@ export const tuiMessages = {
|
|
|
8
9
|
roles: "Roles",
|
|
9
10
|
summary: "Synthese",
|
|
10
11
|
ollamaModel: "Modele Ollama",
|
|
12
|
+
ollamaUrl: "Adresse Ollama configuree",
|
|
13
|
+
ollamaUrlEffective: "Adresse Ollama effective",
|
|
11
14
|
responses: "Tours",
|
|
12
15
|
folder: "Dossier",
|
|
13
16
|
docs: "Docs",
|
|
@@ -24,6 +27,7 @@ export const tuiMessages = {
|
|
|
24
27
|
helpNew: "assistant guide",
|
|
25
28
|
helpRetry: "relancer la derniere session",
|
|
26
29
|
helpHistory: "voir les derniers exports",
|
|
30
|
+
helpUpdate: "voir les informations de mise a jour",
|
|
27
31
|
helpHelp: "aide",
|
|
28
32
|
helpQuit: "quitter",
|
|
29
33
|
helpFallback: "Tape un sujet ou une commande.",
|
|
@@ -78,10 +82,12 @@ export const tuiMessages = {
|
|
|
78
82
|
turnsUsage: "Usage: /turns <tours>",
|
|
79
83
|
summaryUsage: "Usage: /summary <agent|none>",
|
|
80
84
|
ollamaModelUsage: "Usage: /ollama-model <modele>",
|
|
85
|
+
ollamaUrlUsage: "Usage: /ollama-url <url|default>",
|
|
81
86
|
interfaceUsage: "Usage: /interface <tui|terminal>",
|
|
82
87
|
languageUsage: "Usage: /language <fr|en>",
|
|
83
88
|
rolesUsage: "Usage: /roles <role...>",
|
|
84
89
|
ollamaInfoCommand: "afficher modeles installes",
|
|
90
|
+
ollamaUrlCommand: "modifier l'adresse (<url|default>)",
|
|
85
91
|
ollamaSyncCommand: "choisir un modele installe disponible",
|
|
86
92
|
interfaceDefault: (value) => `Interface par defaut: ${value}.`,
|
|
87
93
|
languageUpdated: (value) => `Langue mise a jour: ${value}.`,
|
|
@@ -98,6 +104,7 @@ export const tuiMessages = {
|
|
|
98
104
|
askSummaryAgent: (value) => `Synthese Ask: ${value}.`,
|
|
99
105
|
debateSummaryAgent: (value) => `Synthese Debat: ${value}.`,
|
|
100
106
|
ollamaInfo: (current, installed, api) => `Ollama ${api}. Modele actuel: ${current}. Modeles installes: ${installed}.`,
|
|
107
|
+
ollamaUrlUpdated: (configured, effective) => configured === effective ? `Adresse Ollama mise a jour: ${configured}.` : `Adresse Ollama configuree: ${configured}. Adresse effective via OLLAMA_HOST: ${effective}.`,
|
|
101
108
|
ollamaUnavailable: (baseUrl) => `API Ollama indisponible (${baseUrl}). Lance ollama serve puis reessaie /ollama.`,
|
|
102
109
|
askAgentsUpdated: (value) => `Agents Ask mis a jour: ${value}.`,
|
|
103
110
|
debateAgentsUpdated: (value) => `Agents Debat mis a jour: ${value}.`,
|
|
@@ -114,6 +121,7 @@ export const tuiMessages = {
|
|
|
114
121
|
},
|
|
115
122
|
en: {
|
|
116
123
|
tagline: "Orchestrate conversations between AI agents",
|
|
124
|
+
updateAvailable: (current, latest) => `Update available: ${current} -> ${latest}. Use /update.`,
|
|
117
125
|
modeLabel: (mode) => mode === "ask" ? "Ask" : "Debate",
|
|
118
126
|
modeValue: (mode) => mode === "ask" ? "Ask" : "Debate",
|
|
119
127
|
noValue: "not set",
|
|
@@ -121,6 +129,8 @@ export const tuiMessages = {
|
|
|
121
129
|
roles: "Roles",
|
|
122
130
|
summary: "Summary",
|
|
123
131
|
ollamaModel: "Ollama model",
|
|
132
|
+
ollamaUrl: "Configured Ollama address",
|
|
133
|
+
ollamaUrlEffective: "Effective Ollama address",
|
|
124
134
|
responses: "Turns",
|
|
125
135
|
folder: "Folder",
|
|
126
136
|
docs: "Docs",
|
|
@@ -137,6 +147,7 @@ export const tuiMessages = {
|
|
|
137
147
|
helpNew: "guided assistant",
|
|
138
148
|
helpRetry: "rerun the last session",
|
|
139
149
|
helpHistory: "show recent exports",
|
|
150
|
+
helpUpdate: "show update information",
|
|
140
151
|
helpHelp: "help",
|
|
141
152
|
helpQuit: "quit",
|
|
142
153
|
helpFallback: "Type a topic or a command.",
|
|
@@ -191,10 +202,12 @@ export const tuiMessages = {
|
|
|
191
202
|
turnsUsage: "Usage: /turns <turns>",
|
|
192
203
|
summaryUsage: "Usage: /summary <agent|none>",
|
|
193
204
|
ollamaModelUsage: "Usage: /ollama-model <model>",
|
|
205
|
+
ollamaUrlUsage: "Usage: /ollama-url <url|default>",
|
|
194
206
|
interfaceUsage: "Usage: /interface <tui|terminal>",
|
|
195
207
|
languageUsage: "Usage: /language <fr|en>",
|
|
196
208
|
rolesUsage: "Usage: /roles <role...>",
|
|
197
209
|
ollamaInfoCommand: "show installed models",
|
|
210
|
+
ollamaUrlCommand: "change the address (<url|default>)",
|
|
198
211
|
ollamaSyncCommand: "choose an available installed model",
|
|
199
212
|
interfaceDefault: (value) => `Default interface: ${value}.`,
|
|
200
213
|
languageUpdated: (value) => `Language updated: ${value}.`,
|
|
@@ -211,6 +224,7 @@ export const tuiMessages = {
|
|
|
211
224
|
askSummaryAgent: (value) => `Ask summary: ${value}.`,
|
|
212
225
|
debateSummaryAgent: (value) => `Debate summary: ${value}.`,
|
|
213
226
|
ollamaInfo: (current, installed, api) => `Ollama ${api}. Current model: ${current}. Installed models: ${installed}.`,
|
|
227
|
+
ollamaUrlUpdated: (configured, effective) => configured === effective ? `Ollama address updated: ${configured}.` : `Ollama address configured: ${configured}. Effective address from OLLAMA_HOST: ${effective}.`,
|
|
214
228
|
ollamaUnavailable: (baseUrl) => `Ollama API unavailable (${baseUrl}). Run ollama serve, then try /ollama again.`,
|
|
215
229
|
askAgentsUpdated: (value) => `Ask agents updated: ${value}.`,
|
|
216
230
|
debateAgentsUpdated: (value) => `Debate agents updated: ${value}.`,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
export const DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
|
|
2
|
+
export class OllamaUrlError extends Error {
|
|
3
|
+
kind;
|
|
4
|
+
value;
|
|
5
|
+
protocol;
|
|
6
|
+
constructor(kind, value, protocol) {
|
|
7
|
+
super(kind === "empty"
|
|
8
|
+
? "Invalid Ollama URL: the value is empty."
|
|
9
|
+
: kind === "protocol"
|
|
10
|
+
? `Invalid Ollama URL protocol: ${protocol ?? ""}`
|
|
11
|
+
: `Invalid Ollama URL: ${value}`);
|
|
12
|
+
this.kind = kind;
|
|
13
|
+
this.value = value;
|
|
14
|
+
this.protocol = protocol;
|
|
15
|
+
this.name = "OllamaUrlError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Résout l'adresse client Ollama selon la priorité produit :
|
|
20
|
+
* flag CLI > OLLAMA_HOST > config agent > serveur local par défaut.
|
|
21
|
+
*/
|
|
22
|
+
export function resolveOllamaBaseUrl(sources = {}) {
|
|
23
|
+
const value = firstNonEmpty(sources.cliUrl, sources.envUrl ?? process.env.OLLAMA_HOST, sources.configUrl, DEFAULT_OLLAMA_BASE_URL);
|
|
24
|
+
return normalizeOllamaBaseUrl(value);
|
|
25
|
+
}
|
|
26
|
+
/** Normalise les formats acceptés par Ollama en URL HTTP(S) utilisable par fetch. */
|
|
27
|
+
export function normalizeOllamaBaseUrl(value) {
|
|
28
|
+
const trimmed = value.trim();
|
|
29
|
+
if (!trimmed) {
|
|
30
|
+
throw new OllamaUrlError("empty", value);
|
|
31
|
+
}
|
|
32
|
+
const withHost = trimmed.startsWith(":") ? `127.0.0.1${trimmed}` : trimmed;
|
|
33
|
+
const hasExplicitScheme = withHost.includes("://");
|
|
34
|
+
const withScheme = hasExplicitScheme ? withHost : `http://${withHost}`;
|
|
35
|
+
let url;
|
|
36
|
+
try {
|
|
37
|
+
url = new URL(withScheme);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
throw new OllamaUrlError("invalid", value);
|
|
41
|
+
}
|
|
42
|
+
if (url.protocol !== "http:" && url.protocol !== "https:") {
|
|
43
|
+
throw new OllamaUrlError("protocol", value, url.protocol);
|
|
44
|
+
}
|
|
45
|
+
if (!url.hostname || url.username || url.password || url.search || url.hash) {
|
|
46
|
+
throw new OllamaUrlError("invalid", value);
|
|
47
|
+
}
|
|
48
|
+
if (!hasExplicitScheme && !url.port) {
|
|
49
|
+
url.port = "11434";
|
|
50
|
+
}
|
|
51
|
+
if (url.hostname === "0.0.0.0") {
|
|
52
|
+
url.hostname = "127.0.0.1";
|
|
53
|
+
}
|
|
54
|
+
else if (url.hostname === "[::]") {
|
|
55
|
+
url.hostname = "[::1]";
|
|
56
|
+
}
|
|
57
|
+
return url.toString().replace(/\/+$/, "");
|
|
58
|
+
}
|
|
59
|
+
/** Retourne l'URL configurée pour l'agent Ollama principal, puis le premier agent Ollama. */
|
|
60
|
+
export function configuredOllamaBaseUrl(config) {
|
|
61
|
+
const primary = config.agents["ollama-local"];
|
|
62
|
+
if (primary?.type === "ollama" && primary.baseUrl) {
|
|
63
|
+
return primary.baseUrl;
|
|
64
|
+
}
|
|
65
|
+
const configured = Object.values(config.agents).find((agent) => agent.type === "ollama" && agent.baseUrl);
|
|
66
|
+
return configured?.type === "ollama" ? configured.baseUrl : undefined;
|
|
67
|
+
}
|
|
68
|
+
/** Retourne les URL configurées par nom d'agent Ollama. */
|
|
69
|
+
export function configuredOllamaTargets(config) {
|
|
70
|
+
return Object.fromEntries(Object.entries(config.agents)
|
|
71
|
+
.filter(([, agent]) => agent.type === "ollama")
|
|
72
|
+
.map(([name, agent]) => [name, agent.type === "ollama" ? agent.baseUrl : undefined]));
|
|
73
|
+
}
|
|
74
|
+
function firstNonEmpty(...values) {
|
|
75
|
+
return values.find((value) => Boolean(value?.trim())) ?? DEFAULT_OLLAMA_BASE_URL;
|
|
76
|
+
}
|
package/dist/orchestrator.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createAgent } from "./adapters/index.js";
|
|
2
2
|
import { AdapterError } from "./errors.js";
|
|
3
3
|
import { createTranslator } from "./i18n.js";
|
|
4
|
+
import { OllamaUrlError } from "./ollamaUrl.js";
|
|
4
5
|
export const MAX_ASK_AGENTS = 4;
|
|
5
6
|
/**
|
|
6
7
|
* Point d'entrée de l'orchestration.
|
|
@@ -27,8 +28,8 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
27
28
|
{ name: options.agentB, role: agentBConfig.role, type: agentBConfig.type }
|
|
28
29
|
]);
|
|
29
30
|
const agents = [
|
|
30
|
-
createAgent(options.agentA, agentAConfig),
|
|
31
|
-
createAgent(options.agentB, agentBConfig)
|
|
31
|
+
createAgent(options.agentA, agentAConfig, { ollamaUrl: options.ollamaUrl }),
|
|
32
|
+
createAgent(options.agentB, agentBConfig, { ollamaUrl: options.ollamaUrl })
|
|
32
33
|
];
|
|
33
34
|
const transcript = [];
|
|
34
35
|
let stopReason;
|
|
@@ -73,7 +74,7 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
73
74
|
agent: current.name,
|
|
74
75
|
role: current.role,
|
|
75
76
|
turn
|
|
76
|
-
});
|
|
77
|
+
}, messages);
|
|
77
78
|
renderer?.error(failure);
|
|
78
79
|
return {
|
|
79
80
|
options,
|
|
@@ -126,7 +127,7 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
126
127
|
agent: resolveSummaryAgentName(options),
|
|
127
128
|
role: summaryRole(),
|
|
128
129
|
turn: transcript.length + 1
|
|
129
|
-
});
|
|
130
|
+
}, messages);
|
|
130
131
|
renderer?.error(failure);
|
|
131
132
|
}
|
|
132
133
|
}
|
|
@@ -163,7 +164,7 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
163
164
|
role: agentConfig.role,
|
|
164
165
|
type: agentConfig.type
|
|
165
166
|
})));
|
|
166
|
-
const agents = agentEntries.map(([name, agentConfig]) => createAgent(name, agentConfig));
|
|
167
|
+
const agents = agentEntries.map(([name, agentConfig]) => createAgent(name, agentConfig, { ollamaUrl: options.ollamaUrl }));
|
|
167
168
|
const transcript = [];
|
|
168
169
|
for (let index = 0; index < agents.length; index += 1) {
|
|
169
170
|
const current = agents[index];
|
|
@@ -212,7 +213,7 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
212
213
|
agent: current.name,
|
|
213
214
|
role: current.role,
|
|
214
215
|
turn: response
|
|
215
|
-
});
|
|
216
|
+
}, messages);
|
|
216
217
|
renderer?.error(failure);
|
|
217
218
|
return {
|
|
218
219
|
options,
|
|
@@ -264,7 +265,7 @@ export async function runAsk(config, options, renderer, messages = createTransla
|
|
|
264
265
|
agent: resolveSummaryAgentName(options),
|
|
265
266
|
role: summaryRole(),
|
|
266
267
|
turn: transcript.length + 1
|
|
267
|
-
});
|
|
268
|
+
}, messages);
|
|
268
269
|
renderer?.error(failure);
|
|
269
270
|
}
|
|
270
271
|
}
|
|
@@ -335,7 +336,7 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
335
336
|
if (!summaryConfig) {
|
|
336
337
|
throw new Error(messages.orchestrator.unknownSummaryAgent(summaryAgentName));
|
|
337
338
|
}
|
|
338
|
-
const summaryAgent = createAgent(summaryAgentName, summaryConfig);
|
|
339
|
+
const summaryAgent = createAgent(summaryAgentName, summaryConfig, { ollamaUrl: options.ollamaUrl });
|
|
339
340
|
const role = summaryRole();
|
|
340
341
|
renderer?.summaryStart(summaryAgent.name, role);
|
|
341
342
|
renderer?.thinkingStart(summaryAgent.name, role);
|
|
@@ -393,7 +394,7 @@ function resolveSummaryAgentName(options) {
|
|
|
393
394
|
}
|
|
394
395
|
return options.agentB;
|
|
395
396
|
}
|
|
396
|
-
function toDebateFailure(error, context) {
|
|
397
|
+
function toDebateFailure(error, context, messages) {
|
|
397
398
|
if (error instanceof AdapterError) {
|
|
398
399
|
return {
|
|
399
400
|
phase: context.phase,
|
|
@@ -405,6 +406,18 @@ function toDebateFailure(error, context) {
|
|
|
405
406
|
details: error.details
|
|
406
407
|
};
|
|
407
408
|
}
|
|
409
|
+
if (error instanceof OllamaUrlError) {
|
|
410
|
+
const message = error.kind === "empty"
|
|
411
|
+
? messages.common.ollamaUrlEmpty
|
|
412
|
+
: error.kind === "protocol"
|
|
413
|
+
? messages.common.ollamaUrlProtocol(error.protocol ?? "")
|
|
414
|
+
: messages.common.ollamaUrlInvalid(error.value);
|
|
415
|
+
return {
|
|
416
|
+
...context,
|
|
417
|
+
kind: "unknown",
|
|
418
|
+
message
|
|
419
|
+
};
|
|
420
|
+
}
|
|
408
421
|
return {
|
|
409
422
|
phase: context.phase,
|
|
410
423
|
agent: context.agent,
|
package/dist/presets.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { detectionForCommand } from "./agentRegistry.js";
|
|
1
|
+
import { detectionForCommand, isRetiredAgentName } from "./agentRegistry.js";
|
|
2
2
|
const presets = {
|
|
3
3
|
"codex-claude": {
|
|
4
4
|
agentA: "codex",
|
|
@@ -161,6 +161,26 @@ export function listPresetsWithAvailability(config, discovery, messages) {
|
|
|
161
161
|
};
|
|
162
162
|
});
|
|
163
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Retourne tous les agents de la config avec la même disponibilité que celle
|
|
166
|
+
* utilisée pour les presets. Les intégrations ne doivent pas réimplémenter la
|
|
167
|
+
* découverte des commandes ou des modèles Ollama.
|
|
168
|
+
*/
|
|
169
|
+
export function listAgentsWithAvailability(config, discovery, messages) {
|
|
170
|
+
return Object.entries(config.agents)
|
|
171
|
+
.filter(([name]) => !isRetiredAgentName(name))
|
|
172
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
173
|
+
.map(([name, agentConfig]) => {
|
|
174
|
+
const check = checkAgentAvailability(name, config, discovery, messages);
|
|
175
|
+
return {
|
|
176
|
+
name,
|
|
177
|
+
type: agentConfig.type,
|
|
178
|
+
role: agentConfig.role,
|
|
179
|
+
available: check.available,
|
|
180
|
+
...(check.available ? {} : { unavailableReason: check.reason })
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
}
|
|
164
184
|
/** Recherche inverse : retourne le nom du preset correspondant à une paire `(agentA, agentB)`, ou `undefined`. */
|
|
165
185
|
export function findPresetNameForPair(agentA, agentB) {
|
|
166
186
|
return Object.entries(presets).find(([, preset]) => preset.agentA === agentA && preset.agentB === agentB)?.[0];
|
|
@@ -171,12 +191,13 @@ function checkAgentAvailability(agentName, config, discovery, messages) {
|
|
|
171
191
|
return unavailable(agentName, messages?.presets.missingAgent(agentName) ?? `agent absent de la config: ${agentName}`);
|
|
172
192
|
}
|
|
173
193
|
if (agent.type === "ollama") {
|
|
174
|
-
|
|
175
|
-
|
|
194
|
+
const ollama = discovery.ollamaAgents?.[agentName] ?? discovery.ollama;
|
|
195
|
+
if (!ollama.available) {
|
|
196
|
+
return unavailable(agentName, ollama.commandAvailable
|
|
176
197
|
? messages?.presets.ollamaUnreachable(agentName) ?? `Ollama non joignable pour ${agentName}`
|
|
177
198
|
: messages?.presets.ollamaNotDetected(agentName) ?? `Ollama non détecté pour ${agentName}`);
|
|
178
199
|
}
|
|
179
|
-
if (!
|
|
200
|
+
if (!ollama.models.includes(agent.model)) {
|
|
180
201
|
return unavailable(agentName, messages?.presets.missingOllamaModel(agentName, agent.model) ?? `modèle Ollama absent pour ${agentName}: ${agent.model}`);
|
|
181
202
|
}
|
|
182
203
|
return available(agentName);
|
package/dist/renderers/tui.js
CHANGED
|
@@ -2,6 +2,8 @@ import { createInterface } from "node:readline/promises";
|
|
|
2
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
|
+
import { isRetiredAgentName } from "../agentRegistry.js";
|
|
6
|
+
import { DEFAULT_OLLAMA_BASE_URL, resolveOllamaBaseUrl } from "../ollamaUrl.js";
|
|
5
7
|
const supportsColor = Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
6
8
|
const supportsInteractiveOutput = Boolean(process.stdout.isTTY);
|
|
7
9
|
/** Cree le premier renderer TUI leger, sans dependance UI externe. */
|
|
@@ -35,10 +37,14 @@ export function renderTuiHome(config, _configPath, messages, state = {}) {
|
|
|
35
37
|
const summary = mode === "ask"
|
|
36
38
|
? defaults.askSummaryAgent ?? defaults.summaryAgent ?? messages.tui.lastAskAgent
|
|
37
39
|
: defaults.summaryAgent ?? defaults.agentB ?? "agent B";
|
|
40
|
+
const version = state.version ?? "0.0.0";
|
|
41
|
+
const versionLines = state.latestVersion
|
|
42
|
+
? [dim(`v${version}`), accent(messages.tui.updateAvailable(version, state.latestVersion))]
|
|
43
|
+
: [dim(`v${version}`)];
|
|
38
44
|
const lines = [
|
|
39
45
|
"",
|
|
40
46
|
...centerLogo(viewport, messages),
|
|
41
|
-
...centerBlock(
|
|
47
|
+
...centerBlock(versionLines, viewport),
|
|
42
48
|
"",
|
|
43
49
|
...centerBlock(composerCard([
|
|
44
50
|
`${accent(messages.tui.modeValue(mode))} ${dim("·")} ${mode === "ask" ? askAgents : debateAgents}`,
|
|
@@ -56,6 +62,21 @@ export function renderTuiHome(config, _configPath, messages, state = {}) {
|
|
|
56
62
|
];
|
|
57
63
|
process.stdout.write(lines.join("\n") + "\n");
|
|
58
64
|
}
|
|
65
|
+
/** Affiche les instructions de mise a jour sans quitter le TUI. */
|
|
66
|
+
export function renderTuiUpdate(instructions, messages) {
|
|
67
|
+
if (supportsInteractiveOutput) {
|
|
68
|
+
clearScreen();
|
|
69
|
+
}
|
|
70
|
+
const viewport = viewportWidth();
|
|
71
|
+
const width = surfaceWidth();
|
|
72
|
+
process.stdout.write([
|
|
73
|
+
"",
|
|
74
|
+
...centerLogo(viewport, messages),
|
|
75
|
+
"",
|
|
76
|
+
...centerBlock(card(instructions.split(/\r?\n/), width), viewport),
|
|
77
|
+
""
|
|
78
|
+
].join("\n"));
|
|
79
|
+
}
|
|
59
80
|
/** Affiche l'aide interne du composer TUI. */
|
|
60
81
|
export function renderTuiHelp(messages) {
|
|
61
82
|
if (supportsInteractiveOutput) {
|
|
@@ -80,6 +101,7 @@ export function renderTuiHelp(messages) {
|
|
|
80
101
|
row("/new", messages.tui.helpNew),
|
|
81
102
|
row("/retry", messages.tui.helpRetry),
|
|
82
103
|
row("/history", messages.tui.helpHistory),
|
|
104
|
+
row("/update", messages.tui.helpUpdate),
|
|
83
105
|
row("/home", messages.tui.backCommand),
|
|
84
106
|
row("/help", messages.tui.helpHelp),
|
|
85
107
|
row("/quit", messages.tui.helpQuit),
|
|
@@ -277,6 +299,8 @@ export function renderTuiConfig(config, configPath, mode, messages, state = {})
|
|
|
277
299
|
: defaults.summaryAgent ?? defaults.agentB ?? messages.tui.noValue;
|
|
278
300
|
const ollamaAgent = config.agents["ollama-local"];
|
|
279
301
|
const ollamaModel = ollamaAgent?.type === "ollama" ? ollamaAgent.model : undefined;
|
|
302
|
+
const ollamaUrl = ollamaAgent?.type === "ollama" ? ollamaAgent.baseUrl ?? DEFAULT_OLLAMA_BASE_URL : undefined;
|
|
303
|
+
const ollamaEffectiveUrl = ollamaUrl ? safeEffectiveOllamaUrl(ollamaUrl) : undefined;
|
|
280
304
|
const currentLines = mode === "ask"
|
|
281
305
|
? [
|
|
282
306
|
row(messages.tui.activeAgents, askAgents),
|
|
@@ -310,6 +334,8 @@ export function renderTuiConfig(config, configPath, mode, messages, state = {})
|
|
|
310
334
|
bold(messages.tui.configTitle),
|
|
311
335
|
"",
|
|
312
336
|
row(messages.tui.activeMode, messages.tui.modeValue(mode)),
|
|
337
|
+
...(ollamaUrl ? [row(messages.tui.ollamaUrl, ollamaUrl)] : []),
|
|
338
|
+
...(ollamaEffectiveUrl && ollamaEffectiveUrl !== ollamaUrl ? [row(messages.tui.ollamaUrlEffective, ollamaEffectiveUrl)] : []),
|
|
313
339
|
row(messages.tui.configFile, configPath),
|
|
314
340
|
row(messages.tui.interface, defaults.interface ?? "tui"),
|
|
315
341
|
row(messages.tui.language, config.language ?? "fr"),
|
|
@@ -323,6 +349,7 @@ export function renderTuiConfig(config, configPath, mode, messages, state = {})
|
|
|
323
349
|
...(ollamaModel ? [
|
|
324
350
|
row("/ollama", messages.tui.ollamaInfoCommand),
|
|
325
351
|
row("/ollama-model", messages.tui.ollamaModelUsage),
|
|
352
|
+
row("/ollama-url", messages.tui.ollamaUrlCommand),
|
|
326
353
|
row("/ollama-sync", messages.tui.ollamaSyncCommand),
|
|
327
354
|
""
|
|
328
355
|
] : []),
|
|
@@ -336,6 +363,18 @@ export function renderTuiConfig(config, configPath, mode, messages, state = {})
|
|
|
336
363
|
];
|
|
337
364
|
process.stdout.write(lines.join("\n") + "\n");
|
|
338
365
|
}
|
|
366
|
+
export function parseTuiOllamaUrlCommand(parts, messages) {
|
|
367
|
+
const value = parts[1];
|
|
368
|
+
return value ? { kind: "ollama-url", url: value } : { kind: "unknown", message: messages.tui.ollamaUrlUsage };
|
|
369
|
+
}
|
|
370
|
+
function safeEffectiveOllamaUrl(configUrl) {
|
|
371
|
+
try {
|
|
372
|
+
return resolveOllamaBaseUrl({ configUrl });
|
|
373
|
+
}
|
|
374
|
+
catch {
|
|
375
|
+
return process.env.OLLAMA_HOST?.trim() || configUrl;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
339
378
|
let lastTuiInterruptAt = 0;
|
|
340
379
|
const doubleInterruptMs = 1200;
|
|
341
380
|
function nextInterruptKind() {
|
|
@@ -394,6 +433,9 @@ export async function promptTuiHomeTopic(mode = "debate", messages, options = {}
|
|
|
394
433
|
if (command === "/retry") {
|
|
395
434
|
return { kind: "retry" };
|
|
396
435
|
}
|
|
436
|
+
if (command === "/update") {
|
|
437
|
+
return { kind: "update" };
|
|
438
|
+
}
|
|
397
439
|
if (command === "/historique" || command === "/history") {
|
|
398
440
|
return { kind: "history" };
|
|
399
441
|
}
|
|
@@ -495,6 +537,9 @@ export async function promptTuiConfigCommand(mode, messages) {
|
|
|
495
537
|
const value = parts[1];
|
|
496
538
|
return value ? { kind: "ollama-model", model: value } : { kind: "ollama-info" };
|
|
497
539
|
}
|
|
540
|
+
if (command === "/ollama-url" || command === "/ollama-host") {
|
|
541
|
+
return parseTuiOllamaUrlCommand(parts, messages);
|
|
542
|
+
}
|
|
498
543
|
if (command === "/ollama-model") {
|
|
499
544
|
const value = parts[1];
|
|
500
545
|
return value ? { kind: "ollama-model", model: value } : { kind: "unknown", message: messages.tui.ollamaModelUsage };
|
|
@@ -829,9 +874,9 @@ function activeAgentNamesForMode(config, mode) {
|
|
|
829
874
|
const agents = defaults.askAgents && defaults.askAgents.length > 0
|
|
830
875
|
? defaults.askAgents
|
|
831
876
|
: [defaults.agentA, defaults.agentB].filter((agent) => Boolean(agent));
|
|
832
|
-
return agents.filter((agent) => Boolean(config.agents[agent]));
|
|
877
|
+
return agents.filter((agent) => Boolean(config.agents[agent]) && !isRetiredAgentName(agent));
|
|
833
878
|
}
|
|
834
|
-
return [defaults.agentA, defaults.agentB].filter((agent) =>
|
|
879
|
+
return [defaults.agentA, defaults.agentB].filter((agent) => typeof agent === "string" && Boolean(config.agents[agent]) && !isRetiredAgentName(agent));
|
|
835
880
|
}
|
|
836
881
|
function agentInventoryLine(config, messages) {
|
|
837
882
|
const agents = Object.entries(config.agents)
|
|
@@ -849,15 +894,12 @@ function agentInventoryRows(config, messages) {
|
|
|
849
894
|
}
|
|
850
895
|
return entries.map(([name, agent]) => row(name, `${agent.type} ${dim("·")} ${agent.role}`));
|
|
851
896
|
}
|
|
852
|
-
function isRetiredAgentName(name) {
|
|
853
|
-
return name === "gemini";
|
|
854
|
-
}
|
|
855
897
|
function exampleAgentsForMode(config, mode) {
|
|
856
898
|
const activeAgents = activeAgentNamesForMode(config, mode);
|
|
857
899
|
if (activeAgents.length > 0) {
|
|
858
900
|
return activeAgents;
|
|
859
901
|
}
|
|
860
|
-
const available = Object.keys(config.agents).sort();
|
|
902
|
+
const available = Object.keys(config.agents).filter((agent) => !isRetiredAgentName(agent)).sort();
|
|
861
903
|
return mode === "ask" ? available.slice(0, 3) : available.slice(0, 2);
|
|
862
904
|
}
|
|
863
905
|
function documentationUrl(config) {
|
package/dist/tuiState.js
CHANGED