triflux 4.0.1 → 4.0.2

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.
Files changed (2) hide show
  1. package/hub/bridge.mjs +52 -0
  2. package/package.json +1 -1
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "4.0.1",
3
+ "version": "4.0.2",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
5
  "type": "module",
6
6
  "bin": {