fix : fail to connect agent
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { execFile } from 'node:child_process';
|
||||
import net from 'node:net';
|
||||
import { promisify } from 'node:util';
|
||||
import { WebSocket } from 'ws';
|
||||
import { CONFIG_PATH, type AgentConfig } from './config.ts';
|
||||
|
||||
const run = promisify(execFile);
|
||||
@@ -30,6 +31,67 @@ function probeTcp(host: string, port: number, timeoutMs = 4000): Promise<string
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tente la poignée de main WebSocket réelle, jeton compris.
|
||||
*
|
||||
* On se déconnecte sans envoyer `hello` : le serveur n'attache un agent qu'à
|
||||
* réception de ce message, donc ce test ne peut pas évincer la session du
|
||||
* service en cours d'exécution.
|
||||
*/
|
||||
function probeHandshake(
|
||||
config: AgentConfig,
|
||||
timeoutMs = 8000,
|
||||
): Promise<{ ok: boolean; detail: string }> {
|
||||
return new Promise((resolve) => {
|
||||
let socket: WebSocket;
|
||||
try {
|
||||
socket = new WebSocket(config.serverUrl, {
|
||||
headers: { authorization: `Bearer ${config.token}` },
|
||||
rejectUnauthorized: !config.insecureTls,
|
||||
handshakeTimeout: timeoutMs,
|
||||
});
|
||||
} catch (err) {
|
||||
resolve({ ok: false, detail: err instanceof Error ? err.message : String(err) });
|
||||
return;
|
||||
}
|
||||
|
||||
const finish = (result: { ok: boolean; detail: string }) => {
|
||||
clearTimeout(timer);
|
||||
try {
|
||||
socket.close();
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
resolve(result);
|
||||
};
|
||||
|
||||
const timer = setTimeout(
|
||||
() => finish({ ok: false, detail: 'aucune réponse à la poignée de main' }),
|
||||
timeoutMs + 1000,
|
||||
);
|
||||
|
||||
socket.once('open', () => finish({ ok: true, detail: 'jeton accepté' }));
|
||||
|
||||
socket.once('error', (err: Error) => {
|
||||
const message = err.message;
|
||||
if (message.includes('401')) {
|
||||
finish({
|
||||
ok: false,
|
||||
detail:
|
||||
'JETON REFUSÉ (401). Le serveur ne reconnaît pas ce jeton : il a été régénéré, ' +
|
||||
"ou l'agent a été supprimé. Régénère-le dans le dashboard puis réinstalle avec --reset-identity.",
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (message.includes('certificate') || message.includes('self-signed')) {
|
||||
finish({ ok: false, detail: `certificat TLS rejeté (${message})` });
|
||||
return;
|
||||
}
|
||||
finish({ ok: false, detail: message });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function commandExists(command: string, args: string[]): Promise<boolean> {
|
||||
try {
|
||||
await run(command, args, { timeout: 8000, windowsHide: true });
|
||||
@@ -71,12 +133,20 @@ export async function runDiagnostics(config: AgentConfig): Promise<number> {
|
||||
|
||||
if (serverUrl) {
|
||||
const port = Number(serverUrl.port) || (serverUrl.protocol === 'wss:' ? 443 : 80);
|
||||
const error = await probeTcp(serverUrl.hostname, port);
|
||||
results.push(
|
||||
error
|
||||
? line('fail', 'serveur', `${serverUrl.host} injoignable (${error})`)
|
||||
: line('ok', 'serveur', `${serverUrl.host} joignable`),
|
||||
);
|
||||
const tcpError = await probeTcp(serverUrl.hostname, port);
|
||||
|
||||
if (tcpError) {
|
||||
results.push(line('fail', 'serveur', `${serverUrl.host} injoignable (${tcpError})`));
|
||||
} else {
|
||||
// Le port ouvert ne prouve rien : un jeton refusé produit exactement la
|
||||
// même réponse TCP. On tente donc la vraie poignée de main.
|
||||
const handshake = await probeHandshake(config);
|
||||
results.push(
|
||||
handshake.ok
|
||||
? line('ok', 'serveur', `${serverUrl.host} — jeton accepté`)
|
||||
: line('fail', 'serveur', `${serverUrl.host} — ${handshake.detail}`),
|
||||
);
|
||||
}
|
||||
if (serverUrl.protocol === 'ws:' && !isLoopback(serverUrl.hostname)) {
|
||||
line('warn', 'transport', 'ws:// non chiffré — à réserver à un réseau privé');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user