triflux 4.0.1 → 4.0.3
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/hub/bridge.mjs +52 -0
- package/hub/team/session.mjs +7 -4
- package/package.json +1 -1
- package/scripts/preflight-cache.mjs +2 -2
package/hub/bridge.mjs
CHANGED
|
@@ -8,6 +8,7 @@ import net from 'node:net';
|
|
|
8
8
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
9
9
|
import { join } from 'node:path';
|
|
10
10
|
import { homedir } from 'node:os';
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
11
12
|
import { parseArgs as nodeParseArgs } from 'node:util';
|
|
12
13
|
import { randomUUID } from 'node:crypto';
|
|
13
14
|
import { fileURLToPath } from 'node:url';
|
|
@@ -284,6 +285,37 @@ export function parseJsonSafe(raw, fallback = null) {
|
|
|
284
285
|
}
|
|
285
286
|
}
|
|
286
287
|
|
|
288
|
+
// Hub 자동 재시작 (Pipe+HTTP 모두 실패 시 1회 시도, 최대 4초 대기)
|
|
289
|
+
async function tryRestartHub() {
|
|
290
|
+
const serverPath = join(PROJECT_ROOT, 'hub', 'server.mjs');
|
|
291
|
+
if (!existsSync(serverPath)) return false;
|
|
292
|
+
|
|
293
|
+
try {
|
|
294
|
+
const child = spawn(process.execPath, [serverPath], {
|
|
295
|
+
detached: true,
|
|
296
|
+
stdio: 'ignore',
|
|
297
|
+
windowsHide: true,
|
|
298
|
+
});
|
|
299
|
+
child.unref();
|
|
300
|
+
} catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
for (let i = 0; i < 8; i++) {
|
|
305
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
306
|
+
try {
|
|
307
|
+
const res = await fetch(`${getHubUrl()}/status`, {
|
|
308
|
+
signal: AbortSignal.timeout(1000),
|
|
309
|
+
});
|
|
310
|
+
if (res.ok) {
|
|
311
|
+
const data = await res.json();
|
|
312
|
+
if (data?.hub?.state === 'healthy') return true;
|
|
313
|
+
}
|
|
314
|
+
} catch {}
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
|
|
287
319
|
async function requestHub(operation, body, timeoutMs = 3000, fallback = null) {
|
|
288
320
|
const viaPipe = operation.transport === 'command'
|
|
289
321
|
? await pipeCommand(operation.action, body, timeoutMs)
|
|
@@ -303,6 +335,26 @@ async function requestHub(operation, body, timeoutMs = 3000, fallback = null) {
|
|
|
303
335
|
return { transport: 'http', result: viaHttp };
|
|
304
336
|
}
|
|
305
337
|
|
|
338
|
+
// Hub 재시작 시도 → Pipe/HTTP 재시도
|
|
339
|
+
if (await tryRestartHub()) {
|
|
340
|
+
const retryPipe = operation.transport === 'command'
|
|
341
|
+
? await pipeCommand(operation.action, body, timeoutMs)
|
|
342
|
+
: await pipeQuery(operation.action, body, timeoutMs);
|
|
343
|
+
if (retryPipe) {
|
|
344
|
+
return { transport: 'pipe', result: retryPipe };
|
|
345
|
+
}
|
|
346
|
+
const retryHttp = operation.httpPath
|
|
347
|
+
? await requestJson(operation.httpPath, {
|
|
348
|
+
method: operation.httpMethod || 'POST',
|
|
349
|
+
body: operation.httpMethod === 'GET' ? undefined : body,
|
|
350
|
+
timeoutMs: Math.max(timeoutMs, 5000),
|
|
351
|
+
})
|
|
352
|
+
: null;
|
|
353
|
+
if (retryHttp) {
|
|
354
|
+
return { transport: 'http', result: retryHttp };
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
|
|
306
358
|
if (!fallback) return null;
|
|
307
359
|
const viaFallback = await fallback();
|
|
308
360
|
if (!viaFallback) return null;
|
package/hub/team/session.mjs
CHANGED
|
@@ -22,7 +22,7 @@ const GIT_BASH_CANDIDATES = [
|
|
|
22
22
|
function findGitBashExe() {
|
|
23
23
|
for (const p of GIT_BASH_CANDIDATES) {
|
|
24
24
|
try {
|
|
25
|
-
execSync(`"${p}" --version`, { stdio: "ignore", timeout: 3000 });
|
|
25
|
+
execSync(`"${p}" --version`, { stdio: "ignore", timeout: 3000, windowsHide: true });
|
|
26
26
|
return p;
|
|
27
27
|
} catch {
|
|
28
28
|
// 다음 후보
|
|
@@ -35,7 +35,7 @@ function findGitBashExe() {
|
|
|
35
35
|
export function hasWindowsTerminal() {
|
|
36
36
|
if (process.platform !== "win32") return false;
|
|
37
37
|
try {
|
|
38
|
-
execSync("where wt.exe", { stdio: "ignore", timeout: 3000 });
|
|
38
|
+
execSync("where wt.exe", { stdio: "ignore", timeout: 3000, windowsHide: true });
|
|
39
39
|
return true;
|
|
40
40
|
} catch {
|
|
41
41
|
return false;
|
|
@@ -50,7 +50,7 @@ export function hasWindowsTerminalSession() {
|
|
|
50
50
|
/** tmux 실행 가능 여부 확인 */
|
|
51
51
|
function hasTmux() {
|
|
52
52
|
try {
|
|
53
|
-
execSync("tmux -V", { stdio: "ignore", timeout: 3000 });
|
|
53
|
+
execSync("tmux -V", { stdio: "ignore", timeout: 3000, windowsHide: true });
|
|
54
54
|
return true;
|
|
55
55
|
} catch {
|
|
56
56
|
return false;
|
|
@@ -60,7 +60,7 @@ function hasTmux() {
|
|
|
60
60
|
/** WSL2 내 tmux 사용 가능 여부 (Windows 전용) */
|
|
61
61
|
function hasWslTmux() {
|
|
62
62
|
try {
|
|
63
|
-
execSync("wsl tmux -V", { stdio: "ignore", timeout: 5000 });
|
|
63
|
+
execSync("wsl tmux -V", { stdio: "ignore", timeout: 5000, windowsHide: true });
|
|
64
64
|
return true;
|
|
65
65
|
} catch {
|
|
66
66
|
return false;
|
|
@@ -76,6 +76,7 @@ function hasGitBashTmux() {
|
|
|
76
76
|
encoding: "utf8",
|
|
77
77
|
timeout: 5000,
|
|
78
78
|
stdio: ["ignore", "pipe", "pipe"],
|
|
79
|
+
windowsHide: true,
|
|
79
80
|
});
|
|
80
81
|
return (r.status ?? 1) === 0;
|
|
81
82
|
} catch {
|
|
@@ -130,6 +131,7 @@ function tmux(args, opts = {}) {
|
|
|
130
131
|
encoding: "utf8",
|
|
131
132
|
timeout: 10000,
|
|
132
133
|
stdio: ["pipe", "pipe", "pipe"],
|
|
134
|
+
windowsHide: true,
|
|
133
135
|
...opts,
|
|
134
136
|
});
|
|
135
137
|
if ((r.status ?? 1) !== 0) {
|
|
@@ -145,6 +147,7 @@ function tmux(args, opts = {}) {
|
|
|
145
147
|
encoding: "utf8",
|
|
146
148
|
timeout: 10000,
|
|
147
149
|
stdio: ["pipe", "pipe", "pipe"],
|
|
150
|
+
windowsHide: true,
|
|
148
151
|
...opts,
|
|
149
152
|
});
|
|
150
153
|
return result != null ? result.trim() : "";
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ const CACHE_TTL_MS = 30_000; // 30초
|
|
|
12
12
|
|
|
13
13
|
function checkHub() {
|
|
14
14
|
try {
|
|
15
|
-
const res = execSync("curl -sf http://127.0.0.1:27888/status", { timeout: 3000, encoding: "utf8" });
|
|
15
|
+
const res = execSync("curl -sf http://127.0.0.1:27888/status", { timeout: 3000, encoding: "utf8", windowsHide: true });
|
|
16
16
|
const data = JSON.parse(res);
|
|
17
17
|
return { ok: true, state: data?.hub?.state || "unknown", pid: data?.pid };
|
|
18
18
|
} catch {
|
|
@@ -27,7 +27,7 @@ function checkRoute() {
|
|
|
27
27
|
|
|
28
28
|
function checkCli(name) {
|
|
29
29
|
try {
|
|
30
|
-
const path = execSync(`which ${name} 2>/dev/null || where ${name} 2>nul`, { encoding: "utf8", timeout: 2000 }).trim();
|
|
30
|
+
const path = execSync(`which ${name} 2>/dev/null || where ${name} 2>nul`, { encoding: "utf8", timeout: 2000, windowsHide: true }).trim();
|
|
31
31
|
return { ok: !!path, path };
|
|
32
32
|
} catch {
|
|
33
33
|
return { ok: false };
|