palabre 0.6.3 → 0.7.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/cli-pty.js +14 -0
- package/dist/adapters/cli.js +47 -10
- package/dist/adapters/ollama.js +15 -0
- package/dist/args.js +3 -0
- package/dist/config.js +41 -1
- package/dist/configWizard.js +17 -4
- package/dist/discovery.js +2 -1
- package/dist/doctor.js +14 -0
- package/dist/index.js +86 -29
- package/dist/messages/adapter-errors.js +2 -2
- package/dist/messages/config.js +12 -0
- package/dist/messages/doctor.js +8 -0
- package/dist/messages/orchestrator.js +2 -0
- package/dist/orchestrator.js +44 -2
- package/dist/version.js +54 -0
- package/package.json +1 -1
package/dist/adapters/cli-pty.js
CHANGED
|
@@ -38,6 +38,9 @@ export class CliPtyAdapter {
|
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
async generate(prompt) {
|
|
41
|
+
if (prompt.signal?.aborted) {
|
|
42
|
+
throw cancelledError(this.name);
|
|
43
|
+
}
|
|
41
44
|
const renderedPrompt = formatAgentPrompt(prompt);
|
|
42
45
|
const promptMode = this.config.promptMode ?? "stdin";
|
|
43
46
|
const baseArgs = withModelArgs(this.config.args ?? [], this.config.model, this.config.modelArg ?? "--model");
|
|
@@ -53,6 +56,7 @@ export class CliPtyAdapter {
|
|
|
53
56
|
let term;
|
|
54
57
|
let dataSubscription;
|
|
55
58
|
let exitSubscription;
|
|
59
|
+
let abortListener;
|
|
56
60
|
const maxOutputBytes = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
57
61
|
const finish = (error, exitCode, kill = true) => {
|
|
58
62
|
if (settled)
|
|
@@ -61,6 +65,9 @@ export class CliPtyAdapter {
|
|
|
61
65
|
clearTimeout(hardTimer);
|
|
62
66
|
dataSubscription?.dispose();
|
|
63
67
|
exitSubscription?.dispose();
|
|
68
|
+
if (abortListener) {
|
|
69
|
+
prompt.signal?.removeEventListener("abort", abortListener);
|
|
70
|
+
}
|
|
64
71
|
if (kill) {
|
|
65
72
|
try {
|
|
66
73
|
term.kill();
|
|
@@ -107,6 +114,10 @@ export class CliPtyAdapter {
|
|
|
107
114
|
}));
|
|
108
115
|
return;
|
|
109
116
|
}
|
|
117
|
+
abortListener = () => {
|
|
118
|
+
finish(cancelledError(this.name));
|
|
119
|
+
};
|
|
120
|
+
prompt.signal?.addEventListener("abort", abortListener, { once: true });
|
|
110
121
|
hardTimer = setTimeout(() => {
|
|
111
122
|
finish(new AdapterError("timeout", this.name, `${this.name} timed out after ${this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms`, {
|
|
112
123
|
timeoutMs: this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
@@ -165,6 +176,9 @@ function createPtyExitError(adapterName, exitCode, raw) {
|
|
|
165
176
|
raw
|
|
166
177
|
});
|
|
167
178
|
}
|
|
179
|
+
function cancelledError(adapterName) {
|
|
180
|
+
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
181
|
+
}
|
|
168
182
|
function summarizePtyOutput(output) {
|
|
169
183
|
const cleaned = cleanTerminalOutput(output);
|
|
170
184
|
return cleaned ? cleaned.slice(-1_200) : "aucune sortie PTY capturee.";
|
package/dist/adapters/cli.js
CHANGED
|
@@ -37,6 +37,9 @@ export class CliAdapter {
|
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
39
|
async generate(prompt) {
|
|
40
|
+
if (prompt.signal?.aborted) {
|
|
41
|
+
throw cancelledError(this.name);
|
|
42
|
+
}
|
|
40
43
|
const renderedPrompt = formatAgentPrompt(prompt);
|
|
41
44
|
const promptMode = this.config.promptMode ?? "stdin";
|
|
42
45
|
const baseArgs = withModelArgs(this.config.args ?? [], this.config.model, this.config.modelArg ?? "--model");
|
|
@@ -54,6 +57,7 @@ export class CliAdapter {
|
|
|
54
57
|
let outputBytes = 0;
|
|
55
58
|
let hardTimer;
|
|
56
59
|
let idleTimer;
|
|
60
|
+
let abortListener;
|
|
57
61
|
const maxOutputBytes = this.config.maxOutputBytes ?? DEFAULT_MAX_OUTPUT_BYTES;
|
|
58
62
|
const finish = (error) => {
|
|
59
63
|
if (settled)
|
|
@@ -62,12 +66,20 @@ export class CliAdapter {
|
|
|
62
66
|
clearTimeout(hardTimer);
|
|
63
67
|
if (idleTimer)
|
|
64
68
|
clearTimeout(idleTimer);
|
|
69
|
+
if (abortListener) {
|
|
70
|
+
prompt.signal?.removeEventListener("abort", abortListener);
|
|
71
|
+
}
|
|
65
72
|
if (error) {
|
|
66
73
|
reject(error);
|
|
67
74
|
return;
|
|
68
75
|
}
|
|
69
76
|
const content = cleanCliOutput(stdout);
|
|
70
77
|
if (!content && !this.config.allowEmptyOutput) {
|
|
78
|
+
const knownError = createKnownCliError(this.name, undefined, stderr);
|
|
79
|
+
if (knownError) {
|
|
80
|
+
reject(knownError);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
71
83
|
const detail = stderr.trim() ? ` Stderr: ${stderr.trim()}` : "";
|
|
72
84
|
reject(new AdapterError("empty-output", this.name, `${this.name} produced empty output.${detail}`, {
|
|
73
85
|
stderr: stderr.trim()
|
|
@@ -79,8 +91,13 @@ export class CliAdapter {
|
|
|
79
91
|
raw: stdout
|
|
80
92
|
});
|
|
81
93
|
};
|
|
94
|
+
abortListener = () => {
|
|
95
|
+
killChildProcess(child);
|
|
96
|
+
finish(cancelledError(this.name));
|
|
97
|
+
};
|
|
98
|
+
prompt.signal?.addEventListener("abort", abortListener, { once: true });
|
|
82
99
|
hardTimer = setTimeout(() => {
|
|
83
|
-
child
|
|
100
|
+
killChildProcess(child);
|
|
84
101
|
finish(new AdapterError("timeout", this.name, `${this.name} timed out after ${this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS}ms`, {
|
|
85
102
|
timeoutMs: this.config.timeoutMs ?? DEFAULT_TIMEOUT_MS
|
|
86
103
|
}));
|
|
@@ -91,7 +108,7 @@ export class CliAdapter {
|
|
|
91
108
|
if (idleTimer)
|
|
92
109
|
clearTimeout(idleTimer);
|
|
93
110
|
idleTimer = setTimeout(() => {
|
|
94
|
-
child
|
|
111
|
+
killChildProcess(child);
|
|
95
112
|
finish(new AdapterError("idle-timeout", this.name, `${this.name} stopped producing output for ${this.config.idleTimeoutMs}ms`, { idleTimeoutMs: this.config.idleTimeoutMs }));
|
|
96
113
|
}, this.config.idleTimeoutMs);
|
|
97
114
|
};
|
|
@@ -99,7 +116,7 @@ export class CliAdapter {
|
|
|
99
116
|
child.stdout.on("data", (chunk) => {
|
|
100
117
|
outputBytes += chunk.length;
|
|
101
118
|
if (outputBytes > maxOutputBytes) {
|
|
102
|
-
child
|
|
119
|
+
killChildProcess(child);
|
|
103
120
|
finish(new AdapterError("output-too-large", this.name, `${this.name} produced more than ${maxOutputBytes} bytes of output`, {
|
|
104
121
|
maxOutputBytes,
|
|
105
122
|
outputBytes
|
|
@@ -112,7 +129,7 @@ export class CliAdapter {
|
|
|
112
129
|
child.stderr.on("data", (chunk) => {
|
|
113
130
|
outputBytes += chunk.length;
|
|
114
131
|
if (outputBytes > maxOutputBytes) {
|
|
115
|
-
child
|
|
132
|
+
killChildProcess(child);
|
|
116
133
|
finish(new AdapterError("output-too-large", this.name, `${this.name} produced more than ${maxOutputBytes} bytes of output`, {
|
|
117
134
|
maxOutputBytes,
|
|
118
135
|
outputBytes
|
|
@@ -189,25 +206,29 @@ function normalizeForWindowsStatus(line) {
|
|
|
189
206
|
* Élève en `usage-limit` si le stderr contient un signal de quota/rate-limit connu.
|
|
190
207
|
*/
|
|
191
208
|
function createCliExitError(adapterName, exitCode, stderr) {
|
|
209
|
+
return createKnownCliError(adapterName, exitCode, stderr)
|
|
210
|
+
?? new AdapterError("non-zero-exit", adapterName, `${adapterName} exited with code ${exitCode}: ${summarizeCliError(cleanCliOutput(stderr))}`, {
|
|
211
|
+
exitCode,
|
|
212
|
+
stderr: cleanCliOutput(stderr)
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function createKnownCliError(adapterName, exitCode, stderr) {
|
|
192
216
|
const cleanedStderr = cleanCliOutput(stderr);
|
|
193
217
|
const usageLimitMessage = extractUsageLimitMessage(cleanedStderr);
|
|
194
218
|
const unsupportedModelMessage = extractUnsupportedModelMessage(cleanedStderr);
|
|
195
219
|
if (usageLimitMessage) {
|
|
196
220
|
return new AdapterError("usage-limit", adapterName, `${adapterName} a atteint une limite d'utilisation: ${usageLimitMessage}`, {
|
|
197
|
-
exitCode,
|
|
221
|
+
...(exitCode === undefined ? {} : { exitCode }),
|
|
198
222
|
stderr: cleanedStderr
|
|
199
223
|
});
|
|
200
224
|
}
|
|
201
225
|
if (unsupportedModelMessage) {
|
|
202
226
|
return new AdapterError("unsupported-model", adapterName, `${adapterName} ne peut pas utiliser ce modèle: ${unsupportedModelMessage}`, {
|
|
203
|
-
exitCode,
|
|
227
|
+
...(exitCode === undefined ? {} : { exitCode }),
|
|
204
228
|
stderr: cleanedStderr
|
|
205
229
|
});
|
|
206
230
|
}
|
|
207
|
-
return
|
|
208
|
-
exitCode,
|
|
209
|
-
stderr: cleanedStderr
|
|
210
|
-
});
|
|
231
|
+
return undefined;
|
|
211
232
|
}
|
|
212
233
|
function extractUnsupportedModelMessage(stderr) {
|
|
213
234
|
const lines = uniqueNonEmptyLines(stderr);
|
|
@@ -286,3 +307,19 @@ function clipLine(value, maxLength) {
|
|
|
286
307
|
? value
|
|
287
308
|
: `${value.slice(0, maxLength - 1)}…`;
|
|
288
309
|
}
|
|
310
|
+
function cancelledError(adapterName) {
|
|
311
|
+
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
312
|
+
}
|
|
313
|
+
function killChildProcess(child) {
|
|
314
|
+
if (process.platform === "win32" && child.pid) {
|
|
315
|
+
const killer = spawn("taskkill.exe", ["/PID", String(child.pid), "/T", "/F"], {
|
|
316
|
+
windowsHide: true,
|
|
317
|
+
stdio: "ignore"
|
|
318
|
+
});
|
|
319
|
+
killer.on("error", () => {
|
|
320
|
+
child.kill();
|
|
321
|
+
});
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
child.kill();
|
|
325
|
+
}
|
package/dist/adapters/ollama.js
CHANGED
|
@@ -35,6 +35,9 @@ export class OllamaAdapter {
|
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
async generate(prompt) {
|
|
38
|
+
if (prompt.signal?.aborted) {
|
|
39
|
+
throw cancelledError(this.name);
|
|
40
|
+
}
|
|
38
41
|
const baseUrl = normalizeBaseUrl(this.config.baseUrl ?? "http://localhost:11434");
|
|
39
42
|
if (this.config.validateModel !== false) {
|
|
40
43
|
await this.ensureModelAvailable(baseUrl);
|
|
@@ -43,6 +46,8 @@ export class OllamaAdapter {
|
|
|
43
46
|
await this.unloadOtherRunningModels(baseUrl);
|
|
44
47
|
}
|
|
45
48
|
const controller = new AbortController();
|
|
49
|
+
const abortListener = () => controller.abort();
|
|
50
|
+
prompt.signal?.addEventListener("abort", abortListener, { once: true });
|
|
46
51
|
const timeout = setTimeout(() => controller.abort(), this.config.timeoutMs ?? 120_000);
|
|
47
52
|
try {
|
|
48
53
|
const response = await fetch(`${baseUrl}/api/chat`, {
|
|
@@ -85,8 +90,15 @@ export class OllamaAdapter {
|
|
|
85
90
|
content
|
|
86
91
|
};
|
|
87
92
|
}
|
|
93
|
+
catch (error) {
|
|
94
|
+
if (prompt.signal?.aborted) {
|
|
95
|
+
throw cancelledError(this.name);
|
|
96
|
+
}
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
88
99
|
finally {
|
|
89
100
|
clearTimeout(timeout);
|
|
101
|
+
prompt.signal?.removeEventListener("abort", abortListener);
|
|
90
102
|
}
|
|
91
103
|
}
|
|
92
104
|
/**
|
|
@@ -217,3 +229,6 @@ async function unloadModel(baseUrl, model, signal) {
|
|
|
217
229
|
function normalizeBaseUrl(baseUrl) {
|
|
218
230
|
return baseUrl.replace(/\/$/, "");
|
|
219
231
|
}
|
|
232
|
+
function cancelledError(adapterName) {
|
|
233
|
+
return new AdapterError("cancelled", adapterName, `${adapterName} cancelled by user.`);
|
|
234
|
+
}
|
package/dist/args.js
CHANGED
|
@@ -23,6 +23,8 @@ const FLAG_SPECS = {
|
|
|
23
23
|
apply: { arity: "boolean" },
|
|
24
24
|
"clear-defaults": { arity: "boolean" },
|
|
25
25
|
"sync-agents": { arity: "boolean" },
|
|
26
|
+
"sync-ollama-model": { arity: "boolean" },
|
|
27
|
+
"ollama-models": { arity: "boolean" },
|
|
26
28
|
// Valeur unique.
|
|
27
29
|
"agent-a": { arity: "single" },
|
|
28
30
|
"agent-b": { arity: "single" },
|
|
@@ -30,6 +32,7 @@ const FLAG_SPECS = {
|
|
|
30
32
|
language: { arity: "single" },
|
|
31
33
|
"model-a": { arity: "single" },
|
|
32
34
|
"model-b": { arity: "single" },
|
|
35
|
+
"set-ollama-model": { arity: "single" },
|
|
33
36
|
preset: { arity: "single" },
|
|
34
37
|
"summary-agent": { arity: "single" },
|
|
35
38
|
"summary-model": { arity: "single" },
|
package/dist/config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
-
import { applyDetectedCommands } from "./agentRegistry.js";
|
|
4
|
+
import { applyDetectedCommands, detectedAgentNames } from "./agentRegistry.js";
|
|
5
5
|
export const DEFAULT_CONFIG_PATH = "palabre.config.json";
|
|
6
6
|
export const LEGACY_CONFIG_PATH = "chicane.config.json";
|
|
7
7
|
export const CONFIG_DIR_NAME = ".palabre";
|
|
@@ -196,6 +196,46 @@ export function createConfigFromDiscovery(discovery) {
|
|
|
196
196
|
: { turns: config.defaults?.turns };
|
|
197
197
|
return config;
|
|
198
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Ajoute dans `config.agents` les agents détectés localement mais absents de la config.
|
|
201
|
+
* Mute `config` directement ; l'appelant est responsable de persister la config.
|
|
202
|
+
*/
|
|
203
|
+
export function syncDetectedAgents(config, discovery) {
|
|
204
|
+
const discoveredConfig = createConfigFromDiscovery(discovery);
|
|
205
|
+
const missingAgents = detectedAgentNames(discovery).filter((agentName) => !config.agents[agentName]);
|
|
206
|
+
applyDetectedCommands(config, discovery);
|
|
207
|
+
for (const agentName of missingAgents) {
|
|
208
|
+
config.agents[agentName] = discoveredConfig.agents[agentName];
|
|
209
|
+
}
|
|
210
|
+
return missingAgents;
|
|
211
|
+
}
|
|
212
|
+
export function syncOllamaModel(config, discovery) {
|
|
213
|
+
const agent = config.agents["ollama-local"];
|
|
214
|
+
if (agent?.type !== "ollama" || discovery.ollama.models.length === 0) {
|
|
215
|
+
return undefined;
|
|
216
|
+
}
|
|
217
|
+
if (discovery.ollama.models.includes(agent.model)) {
|
|
218
|
+
return undefined;
|
|
219
|
+
}
|
|
220
|
+
const previousModel = agent.model;
|
|
221
|
+
agent.model = chooseDefaultOllamaModel(discovery);
|
|
222
|
+
return {
|
|
223
|
+
previousModel,
|
|
224
|
+
nextModel: agent.model
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
export function setOllamaModel(config, model) {
|
|
228
|
+
const agent = config.agents["ollama-local"];
|
|
229
|
+
if (agent?.type !== "ollama") {
|
|
230
|
+
return undefined;
|
|
231
|
+
}
|
|
232
|
+
const previousModel = agent.model;
|
|
233
|
+
agent.model = model;
|
|
234
|
+
return {
|
|
235
|
+
previousModel,
|
|
236
|
+
nextModel: agent.model
|
|
237
|
+
};
|
|
238
|
+
}
|
|
199
239
|
/** Écrit `config` sérialisé en JSON dans `configPath`. Crée le répertoire parent si nécessaire. */
|
|
200
240
|
export async function writeExampleConfig(configPath = DEFAULT_CONFIG_PATH, config = exampleConfig) {
|
|
201
241
|
const resolved = path.resolve(configPath);
|
package/dist/configWizard.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createInterface } from "node:readline/promises";
|
|
2
2
|
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
-
import { writeExampleConfig } from "./config.js";
|
|
3
|
+
import { syncDetectedAgents, writeExampleConfig } from "./config.js";
|
|
4
|
+
import { discoverLocalTools } from "./discovery.js";
|
|
4
5
|
import { DEFAULT_TURNS, MAX_TURNS, turnsOrDefault, validateTurns } from "./limits.js";
|
|
5
6
|
/**
|
|
6
7
|
* Lance le wizard interactif de configuration des defaults.
|
|
@@ -26,9 +27,10 @@ export async function runConfigWizard(configPath, config, messages) {
|
|
|
26
27
|
console.log(messages.config.wizardActionQuestion);
|
|
27
28
|
console.log(` 1) ${messages.config.wizardActionSetDefaults}`);
|
|
28
29
|
console.log(` 2) ${messages.config.wizardActionClearDefaults}`);
|
|
29
|
-
console.log(` 3) ${messages.config.
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
console.log(` 3) ${messages.config.wizardActionSyncAgents}`);
|
|
31
|
+
console.log(` 4) ${messages.config.wizardActionExit}`);
|
|
32
|
+
const action = await askChoice(rl, messages.config.wizardChoicePrompt, "1", ["1", "2", "3", "4"], messages);
|
|
33
|
+
if (!action || action === "4") {
|
|
32
34
|
console.log(messages.config.wizardUnchanged);
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
@@ -38,6 +40,17 @@ export async function runConfigWizard(configPath, config, messages) {
|
|
|
38
40
|
console.log(messages.config.wizardCleared(configPath));
|
|
39
41
|
return;
|
|
40
42
|
}
|
|
43
|
+
if (action === "3") {
|
|
44
|
+
const discovery = await discoverLocalTools();
|
|
45
|
+
const addedAgents = syncDetectedAgents(config, discovery);
|
|
46
|
+
if (addedAgents.length === 0) {
|
|
47
|
+
console.log(messages.config.syncNoMissing(configPath));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
await writeExampleConfig(configPath, config);
|
|
51
|
+
console.log(messages.config.syncAdded(configPath, addedAgents.join(", ")));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
41
54
|
const agentA = await askAgent(rl, choices, messages.config.wizardAgentADescription, config.defaults?.agentA, messages);
|
|
42
55
|
if (!agentA)
|
|
43
56
|
return;
|
package/dist/discovery.js
CHANGED
|
@@ -4,13 +4,14 @@ import { executableExtensions } from "./exec.js";
|
|
|
4
4
|
/**
|
|
5
5
|
* Détecte en parallèle toutes les CLIs supportées et le serveur Ollama local.
|
|
6
6
|
* Sur Windows, tente `claude.exe` avant `claude`.
|
|
7
|
+
* Antigravity est exposé selon les installations sous `agy` ou `antigravity`.
|
|
7
8
|
*/
|
|
8
9
|
export async function discoverLocalTools() {
|
|
9
10
|
const [codex, claude, gemini, antigravity, opencode, ollamaCommand] = await Promise.all([
|
|
10
11
|
detectCommand("codex"),
|
|
11
12
|
detectFirstCommand(process.platform === "win32" ? ["claude.exe", "claude"] : ["claude"]),
|
|
12
13
|
detectCommand("gemini"),
|
|
13
|
-
|
|
14
|
+
detectFirstCommand(["agy", "antigravity"]),
|
|
14
15
|
detectCommand("opencode"),
|
|
15
16
|
detectCommand("ollama")
|
|
16
17
|
]);
|
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 { compareSemver, getLatestPackageVersion, getPackageVersion } from "./version.js";
|
|
8
9
|
/**
|
|
9
10
|
* Exécute le diagnostic complet : config, outils locaux et agents.
|
|
10
11
|
* Retourne toujours un résultat (pas de throw) ; les erreurs de config sont reportées comme lignes `error`.
|
|
@@ -20,6 +21,7 @@ export async function runDoctor(explicitConfigPath, plain = false, explicitLangu
|
|
|
20
21
|
});
|
|
21
22
|
const t = createTranslator(language);
|
|
22
23
|
lines.push(info(t.doctor.title, "title"));
|
|
24
|
+
await inspectCliVersion(lines, t);
|
|
23
25
|
lines.push(info(t.doctor.currentDirectory(process.cwd()), "cwd"));
|
|
24
26
|
lines.push(hasConfig
|
|
25
27
|
? ok(t.doctor.configFound(configPath))
|
|
@@ -53,6 +55,18 @@ export async function runDoctor(explicitConfigPath, plain = false, explicitLangu
|
|
|
53
55
|
inspectAgents(config, discovery, lines, t);
|
|
54
56
|
return render(lines, plain, t);
|
|
55
57
|
}
|
|
58
|
+
async function inspectCliVersion(lines, t) {
|
|
59
|
+
const currentVersion = await getPackageVersion();
|
|
60
|
+
lines.push(info(t.doctor.cliVersion(currentVersion)));
|
|
61
|
+
const latestVersion = await getLatestPackageVersion();
|
|
62
|
+
if (!latestVersion) {
|
|
63
|
+
lines.push(info(t.doctor.updateUnknown));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
lines.push(compareSemver(currentVersion, latestVersion) < 0
|
|
67
|
+
? warn(t.doctor.updateAvailable(currentVersion, latestVersion))
|
|
68
|
+
: ok(t.doctor.updateCurrent(latestVersion)));
|
|
69
|
+
}
|
|
56
70
|
async function loadConfigSafely(configPath) {
|
|
57
71
|
try {
|
|
58
72
|
return await loadConfig(configPath);
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, writeExampleConfig } from "./config.js";
|
|
2
|
+
import { assertRunnableConfig, configExists, createConfigFromDiscovery, DEFAULT_CONFIG_PATH, GLOBAL_CONFIG_PATH, loadConfig, resolveDefaultConfigPath, resolveOutputDir, setOllamaModel, syncDetectedAgents, syncOllamaModel, writeExampleConfig } from "./config.js";
|
|
6
3
|
import { loadProjectInputs } from "./context.js";
|
|
7
4
|
import { buildContextScan } from "./contextScan.js";
|
|
8
5
|
import { discoverLocalTools } from "./discovery.js";
|
|
@@ -22,6 +19,7 @@ import { applySourceUpdate, formatUpdateInstructions, getUpdateInfo } from "./up
|
|
|
22
19
|
import { createSessionContext } from "./session.js";
|
|
23
20
|
import { getStringListFlag, parseArgs } from "./args.js";
|
|
24
21
|
import { detectedAgentNames, detectionForCommand } from "./agentRegistry.js";
|
|
22
|
+
import { getPackageVersion } from "./version.js";
|
|
25
23
|
/** Point d'entrée principal du CLI Palabre. Dispatche vers la commande appropriée selon les arguments. */
|
|
26
24
|
async function main() {
|
|
27
25
|
const rawArgs = process.argv.slice(2);
|
|
@@ -166,7 +164,8 @@ async function main() {
|
|
|
166
164
|
summaryModel: optionalString(parsed.flags["summary-model"]),
|
|
167
165
|
summaryEnabled: !parsed.flags["no-summary"],
|
|
168
166
|
earlyStopOnAgreement: !parsed.flags["no-early-stop"],
|
|
169
|
-
plainOutput: Boolean(parsed.flags.plain)
|
|
167
|
+
plainOutput: Boolean(parsed.flags.plain),
|
|
168
|
+
signal: debateAbortSignal()
|
|
170
169
|
};
|
|
171
170
|
if (parsed.flags["show-prompt"]) {
|
|
172
171
|
printContextWarnings(context.warnings, messages);
|
|
@@ -179,9 +178,20 @@ async function main() {
|
|
|
179
178
|
const outputPath = await writeDebateMarkdown(resolveOutputDir(config.outputDir), result.options, result.messages, result.summary, result.stopReason, messages, result.failure);
|
|
180
179
|
renderer.done(outputPath);
|
|
181
180
|
if (result.failure) {
|
|
182
|
-
process.exitCode = 1;
|
|
181
|
+
process.exitCode = result.failure.kind === "cancelled" ? 130 : 1;
|
|
183
182
|
}
|
|
184
183
|
}
|
|
184
|
+
function debateAbortSignal() {
|
|
185
|
+
const controller = new AbortController();
|
|
186
|
+
const abort = () => {
|
|
187
|
+
if (!controller.signal.aborted) {
|
|
188
|
+
controller.abort();
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
process.once("SIGINT", abort);
|
|
192
|
+
process.once("SIGTERM", abort);
|
|
193
|
+
return controller.signal;
|
|
194
|
+
}
|
|
185
195
|
/**
|
|
186
196
|
* Exécute la commande `agents` : charge la config et affiche les agents déclarés avec leur état de détection.
|
|
187
197
|
* @param flags - Flags parsés depuis la ligne de commande.
|
|
@@ -220,6 +230,19 @@ async function runConfigCommand(flags) {
|
|
|
220
230
|
configLanguage: config.language
|
|
221
231
|
});
|
|
222
232
|
const messages = createTranslator(language);
|
|
233
|
+
if (flags["ollama-models"]) {
|
|
234
|
+
await runOllamaModelsCommand(config, Boolean(flags.json));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const setOllamaModelValue = optionalString(flags["set-ollama-model"]);
|
|
238
|
+
if (setOllamaModelValue !== undefined) {
|
|
239
|
+
await runSetOllamaModelCommand(configPath, config, setOllamaModelValue, messages);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (flags["sync-ollama-model"]) {
|
|
243
|
+
await runSyncOllamaModelCommand(configPath, config, messages);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
223
246
|
if (flags["sync-agents"]) {
|
|
224
247
|
const discovery = await discoverLocalTools();
|
|
225
248
|
const addedAgents = syncDetectedAgents(config, discovery);
|
|
@@ -278,6 +301,63 @@ async function runConfigCommand(flags) {
|
|
|
278
301
|
}
|
|
279
302
|
await runConfigWizard(configPath, config, messages);
|
|
280
303
|
}
|
|
304
|
+
async function runOllamaModelsCommand(config, json) {
|
|
305
|
+
const discovery = await discoverLocalTools();
|
|
306
|
+
const agent = config.agents["ollama-local"];
|
|
307
|
+
const currentModel = agent?.type === "ollama" ? agent.model : null;
|
|
308
|
+
const payload = {
|
|
309
|
+
v: 1,
|
|
310
|
+
agent: "ollama-local",
|
|
311
|
+
available: discovery.ollama.available,
|
|
312
|
+
baseUrl: discovery.ollama.baseUrl,
|
|
313
|
+
currentModel,
|
|
314
|
+
currentModelInstalled: currentModel ? discovery.ollama.models.includes(currentModel) : false,
|
|
315
|
+
installedModels: discovery.ollama.models
|
|
316
|
+
};
|
|
317
|
+
if (json) {
|
|
318
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
console.log(`ollama-local: ${currentModel ?? "(non configuré)"}`);
|
|
322
|
+
console.log(`Ollama API: ${discovery.ollama.available ? "joignable" : "indisponible"} (${discovery.ollama.baseUrl})`);
|
|
323
|
+
console.log(`Modèles installés: ${discovery.ollama.models.length > 0 ? discovery.ollama.models.join(", ") : "(aucun)"}`);
|
|
324
|
+
}
|
|
325
|
+
async function runSetOllamaModelCommand(configPath, config, model, messages) {
|
|
326
|
+
const trimmed = model.trim();
|
|
327
|
+
if (!trimmed) {
|
|
328
|
+
throw new Error(messages.common.optionRequiresValue("--set-ollama-model"));
|
|
329
|
+
}
|
|
330
|
+
const discovery = await discoverLocalTools();
|
|
331
|
+
const agent = config.agents["ollama-local"];
|
|
332
|
+
if (agent?.type !== "ollama") {
|
|
333
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
334
|
+
}
|
|
335
|
+
if (!discovery.ollama.models.includes(trimmed)) {
|
|
336
|
+
throw new Error(messages.config.ollamaModelUnavailable(trimmed));
|
|
337
|
+
}
|
|
338
|
+
const result = setOllamaModel(config, trimmed);
|
|
339
|
+
await writeExampleConfig(configPath, config);
|
|
340
|
+
console.log(result
|
|
341
|
+
? messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel)
|
|
342
|
+
: messages.config.ollamaModelNoChange(configPath, agent.model));
|
|
343
|
+
}
|
|
344
|
+
async function runSyncOllamaModelCommand(configPath, config, messages) {
|
|
345
|
+
const discovery = await discoverLocalTools();
|
|
346
|
+
const agent = config.agents["ollama-local"];
|
|
347
|
+
if (agent?.type !== "ollama") {
|
|
348
|
+
throw new Error(messages.config.ollamaModelNoAgent);
|
|
349
|
+
}
|
|
350
|
+
if (discovery.ollama.models.length === 0) {
|
|
351
|
+
throw new Error(messages.config.ollamaModelNoInstalledModels);
|
|
352
|
+
}
|
|
353
|
+
const result = syncOllamaModel(config, discovery);
|
|
354
|
+
if (!result) {
|
|
355
|
+
console.log(messages.config.ollamaModelNoChange(configPath, agent.model));
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
await writeExampleConfig(configPath, config);
|
|
359
|
+
console.log(messages.config.ollamaModelUpdated(configPath, result.previousModel, result.nextModel));
|
|
360
|
+
}
|
|
281
361
|
/**
|
|
282
362
|
* Renvoie `true` si la valeur représente une désactivation explicite (ex. "none", "0", "disabled").
|
|
283
363
|
* @param value - Chaîne saisie par l'utilisateur.
|
|
@@ -472,13 +552,6 @@ async function runContextCommand(flags, positionals) {
|
|
|
472
552
|
console.error(`${messages.renderers.warningPrefix} ${warning}`);
|
|
473
553
|
}
|
|
474
554
|
}
|
|
475
|
-
/** Lit la version depuis `package.json` adjacent au bundle compilé. */
|
|
476
|
-
async function getPackageVersion() {
|
|
477
|
-
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
478
|
-
const raw = await readFile(packageJsonPath, "utf8");
|
|
479
|
-
const packageJson = JSON.parse(raw);
|
|
480
|
-
return packageJson.version ?? "0.0.0";
|
|
481
|
-
}
|
|
482
555
|
/**
|
|
483
556
|
* Écrit les avertissements de contexte sur `stderr`.
|
|
484
557
|
* @param warnings - Messages d'avertissement issus du chargement des fichiers de contexte.
|
|
@@ -495,22 +568,6 @@ function printContextWarnings(warnings, messages) {
|
|
|
495
568
|
* @param discovery - Résultat de la découverte locale des outils.
|
|
496
569
|
* @returns Noms des agents nouvellement ajoutés.
|
|
497
570
|
*/
|
|
498
|
-
function syncDetectedAgents(config, discovery) {
|
|
499
|
-
const discoveredConfig = createConfigFromDiscovery(discovery);
|
|
500
|
-
const missingAgents = findDetectedMissingAgents(config, discovery);
|
|
501
|
-
for (const agentName of missingAgents) {
|
|
502
|
-
config.agents[agentName] = discoveredConfig.agents[agentName];
|
|
503
|
-
}
|
|
504
|
-
return missingAgents;
|
|
505
|
-
}
|
|
506
|
-
/**
|
|
507
|
-
* Renvoie les noms des agents détectés localement qui ne sont pas encore dans `config.agents`.
|
|
508
|
-
* @param config - Config Palabre existante.
|
|
509
|
-
* @param discovery - Résultat de la découverte locale des outils.
|
|
510
|
-
*/
|
|
511
|
-
function findDetectedMissingAgents(config, discovery) {
|
|
512
|
-
return detectedAgentNames(discovery).filter((agentName) => !config.agents[agentName]);
|
|
513
|
-
}
|
|
514
571
|
/**
|
|
515
572
|
* Affiche la liste des agents déclarés avec leur type, rôle, état de détection et défauts.
|
|
516
573
|
* @param configPath - Chemin du fichier de config (affiché en en-tête).
|
|
@@ -8,7 +8,7 @@ const frHints = {
|
|
|
8
8
|
"usage-limit": "Attends la fenetre indiquee par la CLI, change de modele ou relance avec un autre agent/preset disponible.",
|
|
9
9
|
"non-zero-exit": "Teste la commande directement, puis ajuste args, permissions, modele ou authentification de la CLI.",
|
|
10
10
|
"model-unavailable": "Installe le modele Ollama ou relance avec --pull-models pour autoriser le telechargement.",
|
|
11
|
-
"unsupported-model": "
|
|
11
|
+
"unsupported-model": "Mets a jour la CLI de l'agent, verifie le nom du modele et ton abonnement, ou retire --model-a/--model-b/--summary-model pour laisser la CLI utiliser son modele par defaut.",
|
|
12
12
|
"model-pull-failed": "Verifie le nom du modele, ta connexion et l'espace disque disponible.",
|
|
13
13
|
"http-error": "Verifie que le service local est lance et que baseUrl est correct."
|
|
14
14
|
};
|
|
@@ -22,7 +22,7 @@ const enHints = {
|
|
|
22
22
|
"usage-limit": "Wait for the window indicated by the CLI, change model, or run again with another available agent/preset.",
|
|
23
23
|
"non-zero-exit": "Test the command directly, then adjust args, permissions, model, or CLI authentication.",
|
|
24
24
|
"model-unavailable": "Install the Ollama model or run again with --pull-models to allow downloading.",
|
|
25
|
-
"unsupported-model": "
|
|
25
|
+
"unsupported-model": "Update the agent CLI, check the model name and your subscription, or remove --model-a/--model-b/--summary-model so the CLI can use its default model.",
|
|
26
26
|
"model-pull-failed": "Check the model name, your connection, and available disk space.",
|
|
27
27
|
"http-error": "Check that the local service is running and baseUrl is correct."
|
|
28
28
|
};
|
package/dist/messages/config.js
CHANGED
|
@@ -3,6 +3,11 @@ export const configMessages = {
|
|
|
3
3
|
createdForConfig: (path) => `${path} créé. Édite la config puis relance palabre config.`,
|
|
4
4
|
syncNoMissing: (path) => `Aucun agent détecté manquant dans ${path}.`,
|
|
5
5
|
syncAdded: (path, agents) => `Agents ajoutés dans ${path}: ${agents}.`,
|
|
6
|
+
ollamaModelNoChange: (path, model) => `Modèle Ollama inchangé dans ${path}: ${model ?? "aucun"}.`,
|
|
7
|
+
ollamaModelUpdated: (path, previousModel, nextModel) => `Modèle Ollama mis à jour dans ${path}: ${previousModel} -> ${nextModel}.`,
|
|
8
|
+
ollamaModelUnavailable: (model) => `Modèle Ollama non installé: ${model}. Action: choisis un modèle installé ou lance \`ollama pull ${model}\`.`,
|
|
9
|
+
ollamaModelNoAgent: "Agent ollama-local absent ou invalide dans la config.",
|
|
10
|
+
ollamaModelNoInstalledModels: "Aucun modèle Ollama installé détecté. Action: lance `ollama pull <modèle>`.",
|
|
6
11
|
updated: (path, defaults, language) => `Configuration mise à jour dans ${path}: ${defaults}, langue: ${language}.`,
|
|
7
12
|
cleared: (path) => `Paramètres par défaut supprimés dans ${path}. Utilise maintenant un preset ou --agent-a/--agent-b pour lancer un débat.`,
|
|
8
13
|
defaultsSummary: (agentA, agentB, turns, summaryAgent) => {
|
|
@@ -19,6 +24,7 @@ export const configMessages = {
|
|
|
19
24
|
wizardActionQuestion: "Que veux-tu faire ?",
|
|
20
25
|
wizardActionSetDefaults: "Définir des paramètres par défaut",
|
|
21
26
|
wizardActionClearDefaults: "Supprimer les paramètres par défaut",
|
|
27
|
+
wizardActionSyncAgents: "Synchroniser les agents détectés",
|
|
22
28
|
wizardActionExit: "Quitter sans modifier",
|
|
23
29
|
wizardChoicePrompt: "Tape le numéro de ton choix",
|
|
24
30
|
wizardChoiceQuestion: (label, defaultValue) => `${label} (Entrée = ${defaultValue}) : `,
|
|
@@ -46,6 +52,11 @@ export const configMessages = {
|
|
|
46
52
|
createdForConfig: (path) => `${path} created. Edit the config, then run palabre config again.`,
|
|
47
53
|
syncNoMissing: (path) => `No missing detected agent in ${path}.`,
|
|
48
54
|
syncAdded: (path, agents) => `Agents added to ${path}: ${agents}.`,
|
|
55
|
+
ollamaModelNoChange: (path, model) => `Ollama model unchanged in ${path}: ${model ?? "none"}.`,
|
|
56
|
+
ollamaModelUpdated: (path, previousModel, nextModel) => `Ollama model updated in ${path}: ${previousModel} -> ${nextModel}.`,
|
|
57
|
+
ollamaModelUnavailable: (model) => `Ollama model is not installed: ${model}. Action: choose an installed model or run \`ollama pull ${model}\`.`,
|
|
58
|
+
ollamaModelNoAgent: "ollama-local agent is missing or invalid in the config.",
|
|
59
|
+
ollamaModelNoInstalledModels: "No installed Ollama model detected. Action: run `ollama pull <model>`.",
|
|
49
60
|
updated: (path, defaults, language) => `Configuration updated in ${path}: ${defaults}, language: ${language}.`,
|
|
50
61
|
cleared: (path) => `Default settings cleared in ${path}. Use a preset or --agent-a/--agent-b to start a debate now.`,
|
|
51
62
|
defaultsSummary: (agentA, agentB, turns, summaryAgent) => {
|
|
@@ -62,6 +73,7 @@ export const configMessages = {
|
|
|
62
73
|
wizardActionQuestion: "What do you want to do?",
|
|
63
74
|
wizardActionSetDefaults: "Set default settings",
|
|
64
75
|
wizardActionClearDefaults: "Clear default settings",
|
|
76
|
+
wizardActionSyncAgents: "Sync detected agents",
|
|
65
77
|
wizardActionExit: "Exit without changes",
|
|
66
78
|
wizardChoicePrompt: "Type the number of your choice",
|
|
67
79
|
wizardChoiceQuestion: (label, defaultValue) => `${label} (Enter = ${defaultValue}): `,
|
package/dist/messages/doctor.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export const doctorMessages = {
|
|
2
2
|
fr: {
|
|
3
3
|
title: "PALABRE doctor",
|
|
4
|
+
cliVersion: (version) => `Version CLI: ${version}`,
|
|
5
|
+
updateCurrent: (version) => `Mise à jour: CLI à jour (${version}).`,
|
|
6
|
+
updateAvailable: (current, latest) => `Mise à jour disponible: ${current} -> ${latest}. Action: lance ` + "`palabre update`.",
|
|
7
|
+
updateUnknown: "Mise à jour: vérification npm indisponible. Action: lance `palabre update` pour les instructions.",
|
|
4
8
|
currentDirectory: (cwd) => `Dossier courant: ${cwd}`,
|
|
5
9
|
configFound: (path) => `Config trouvée: ${path}`,
|
|
6
10
|
configMissing: (path) => `Config absente: ${path}`,
|
|
@@ -63,6 +67,10 @@ export const doctorMessages = {
|
|
|
63
67
|
},
|
|
64
68
|
en: {
|
|
65
69
|
title: "PALABRE doctor",
|
|
70
|
+
cliVersion: (version) => `CLI version: ${version}`,
|
|
71
|
+
updateCurrent: (version) => `Update: CLI is up to date (${version}).`,
|
|
72
|
+
updateAvailable: (current, latest) => `Update available: ${current} -> ${latest}. Action: run ` + "`palabre update`.",
|
|
73
|
+
updateUnknown: "Update: npm check unavailable. Action: run `palabre update` for instructions.",
|
|
66
74
|
currentDirectory: (cwd) => `Current directory: ${cwd}`,
|
|
67
75
|
configFound: (path) => `Config found: ${path}`,
|
|
68
76
|
configMissing: (path) => `Config missing: ${path}`,
|
|
@@ -11,6 +11,7 @@ export const orchestratorMessages = {
|
|
|
11
11
|
"rien a ajouter",
|
|
12
12
|
"question factuelle resolue"
|
|
13
13
|
],
|
|
14
|
+
cancelled: "Débat annulé par l'utilisateur.",
|
|
14
15
|
ollamaNoContext: (agentNames) => `${agentNames} ne lit pas le filesystem. Ajoute --files ou --context pour fournir un contexte projet.`,
|
|
15
16
|
unknownSummaryAgent: (agentName) => `Agent de synthese inconnu: ${agentName}`
|
|
16
17
|
},
|
|
@@ -27,6 +28,7 @@ export const orchestratorMessages = {
|
|
|
27
28
|
"nothing to add",
|
|
28
29
|
"factual question resolved"
|
|
29
30
|
],
|
|
31
|
+
cancelled: "Debate cancelled by the user.",
|
|
30
32
|
ollamaNoContext: (agentNames) => `${agentNames} cannot read the filesystem. Add --files or --context to provide project context.`,
|
|
31
33
|
unknownSummaryAgent: (agentName) => `Unknown summary agent: ${agentName}`
|
|
32
34
|
}
|
package/dist/orchestrator.js
CHANGED
|
@@ -32,6 +32,19 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
32
32
|
const transcript = [];
|
|
33
33
|
let stopReason;
|
|
34
34
|
for (let index = 0; index < options.turns; index += 1) {
|
|
35
|
+
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
36
|
+
phase: "debate",
|
|
37
|
+
turn: index + 1
|
|
38
|
+
});
|
|
39
|
+
if (cancellation) {
|
|
40
|
+
renderer?.error(cancellation);
|
|
41
|
+
return {
|
|
42
|
+
options,
|
|
43
|
+
messages: transcript,
|
|
44
|
+
stopReason,
|
|
45
|
+
failure: cancellation
|
|
46
|
+
};
|
|
47
|
+
}
|
|
35
48
|
const current = agents[index % agents.length];
|
|
36
49
|
const peer = agents[(index + 1) % agents.length];
|
|
37
50
|
const turn = index + 1;
|
|
@@ -49,7 +62,8 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
49
62
|
language: options.language,
|
|
50
63
|
session: options.session,
|
|
51
64
|
files: options.files,
|
|
52
|
-
transcript
|
|
65
|
+
transcript,
|
|
66
|
+
signal: options.signal
|
|
53
67
|
});
|
|
54
68
|
}
|
|
55
69
|
catch (error) {
|
|
@@ -88,6 +102,20 @@ export async function runDebate(config, options, renderer, messages = createTran
|
|
|
88
102
|
let failure;
|
|
89
103
|
if (options.summaryEnabled) {
|
|
90
104
|
try {
|
|
105
|
+
const cancellation = cancellationFailureIfAborted(options, messages, {
|
|
106
|
+
phase: "summary",
|
|
107
|
+
agent: options.summaryAgent ?? options.agentB,
|
|
108
|
+
turn: transcript.length + 1
|
|
109
|
+
});
|
|
110
|
+
if (cancellation) {
|
|
111
|
+
renderer?.error(cancellation);
|
|
112
|
+
return {
|
|
113
|
+
options,
|
|
114
|
+
messages: transcript,
|
|
115
|
+
stopReason,
|
|
116
|
+
failure: cancellation
|
|
117
|
+
};
|
|
118
|
+
}
|
|
91
119
|
summary = await generateSummary(config, options, transcript, renderer, messages);
|
|
92
120
|
}
|
|
93
121
|
catch (error) {
|
|
@@ -181,7 +209,8 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
181
209
|
language: options.language,
|
|
182
210
|
session: options.session,
|
|
183
211
|
files: options.files,
|
|
184
|
-
transcript
|
|
212
|
+
transcript,
|
|
213
|
+
signal: options.signal
|
|
185
214
|
}).finally(() => renderer?.thinkingEnd());
|
|
186
215
|
const summary = {
|
|
187
216
|
agent: summaryAgent.name,
|
|
@@ -192,6 +221,19 @@ async function generateSummary(config, options, transcript, renderer, messages =
|
|
|
192
221
|
renderer?.message(summary.content);
|
|
193
222
|
return summary;
|
|
194
223
|
}
|
|
224
|
+
function cancellationFailureIfAborted(options, messages, context) {
|
|
225
|
+
if (!options.signal?.aborted) {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
phase: context.phase,
|
|
230
|
+
agent: context.agent,
|
|
231
|
+
role: context.role,
|
|
232
|
+
turn: context.turn,
|
|
233
|
+
kind: "cancelled",
|
|
234
|
+
message: messages.orchestrator.cancelled
|
|
235
|
+
};
|
|
236
|
+
}
|
|
195
237
|
function toDebateFailure(error, context) {
|
|
196
238
|
if (error instanceof AdapterError) {
|
|
197
239
|
return {
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
/** Lit la version depuis `package.json` adjacent au bundle compilé. */
|
|
5
|
+
export async function getPackageVersion() {
|
|
6
|
+
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
7
|
+
const raw = await readFile(packageJsonPath, "utf8");
|
|
8
|
+
const packageJson = JSON.parse(raw);
|
|
9
|
+
return packageJson.version ?? "0.0.0";
|
|
10
|
+
}
|
|
11
|
+
/** Compare deux versions semver simples `major.minor.patch`. */
|
|
12
|
+
export function compareSemver(left, right) {
|
|
13
|
+
const a = parseSemverParts(left);
|
|
14
|
+
const b = parseSemverParts(right);
|
|
15
|
+
for (let index = 0; index < 3; index += 1) {
|
|
16
|
+
const diff = a[index] - b[index];
|
|
17
|
+
if (diff !== 0) {
|
|
18
|
+
return diff;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return 0;
|
|
22
|
+
}
|
|
23
|
+
/** Lit la dernière version publiée sur npm. Retourne `undefined` hors ligne ou si le registre ne répond pas. */
|
|
24
|
+
export async function getLatestPackageVersion(timeoutMs = 1_500) {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeout = setTimeout(() => controller.abort(), timeoutMs);
|
|
27
|
+
try {
|
|
28
|
+
const response = await fetch("https://registry.npmjs.org/palabre/latest", {
|
|
29
|
+
signal: controller.signal,
|
|
30
|
+
headers: {
|
|
31
|
+
accept: "application/json"
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
const data = await response.json();
|
|
38
|
+
return typeof data.version === "string" ? data.version : undefined;
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
finally {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function parseSemverParts(value) {
|
|
48
|
+
const match = value.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
49
|
+
return [
|
|
50
|
+
Number(match?.[1] ?? 0),
|
|
51
|
+
Number(match?.[2] ?? 0),
|
|
52
|
+
Number(match?.[3] ?? 0)
|
|
53
|
+
];
|
|
54
|
+
}
|