@triflux/remote 10.50.2 → 10.51.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.
Files changed (97) hide show
  1. package/cto/brief.mjs +0 -12
  2. package/cto/collect.mjs +0 -73
  3. package/cto/current.schema.json +0 -4
  4. package/cto/status.mjs +2 -50
  5. package/hub/index.mjs +0 -8
  6. package/hub/team/agent-map.json +0 -1
  7. package/hub/team/ansi.mjs +1 -35
  8. package/hub/team/backend.mjs +9 -145
  9. package/hub/team/cli/commands/attach.mjs +1 -17
  10. package/hub/team/cli/commands/control.mjs +12 -38
  11. package/hub/team/cli/commands/debug.mjs +1 -37
  12. package/hub/team/cli/commands/focus.mjs +5 -31
  13. package/hub/team/cli/commands/interrupt.mjs +6 -25
  14. package/hub/team/cli/commands/kill.mjs +1 -30
  15. package/hub/team/cli/commands/list.mjs +0 -16
  16. package/hub/team/cli/commands/send.mjs +6 -26
  17. package/hub/team/cli/commands/start/index.mjs +41 -133
  18. package/hub/team/cli/commands/start/parse-args.mjs +15 -40
  19. package/hub/team/cli/commands/start/start-headless.mjs +138 -105
  20. package/hub/team/cli/commands/start/start-mux.mjs +0 -3
  21. package/hub/team/cli/commands/status.mjs +2 -57
  22. package/hub/team/cli/commands/stop.mjs +14 -19
  23. package/hub/team/cli/help.mjs +4 -5
  24. package/hub/team/cli/manifest.mjs +5 -1
  25. package/hub/team/cli/render.mjs +0 -12
  26. package/hub/team/cli/services/hub-client.mjs +2 -88
  27. package/hub/team/cli/services/runtime-mode.mjs +27 -98
  28. package/hub/team/handoff.mjs +3 -34
  29. package/hub/team/headless.mjs +13 -754
  30. package/hub/team/orchestrator.mjs +4 -30
  31. package/hub/team/pane.mjs +6 -28
  32. package/hub/team/psmux.mjs +0 -1
  33. package/hub/team/remote-session.mjs +1 -2
  34. package/hub/team/session.mjs +7 -91
  35. package/hub/team/swarm-intent.mjs +0 -198
  36. package/hub/team/tui-core.mjs +1 -1
  37. package/hub/team/tui-viewer.mjs +2 -2
  38. package/hub/team/tui.mjs +8 -269
  39. package/hub/team/wt-manager.mjs +2 -2
  40. package/hub/workers/factory.mjs +4 -37
  41. package/hub/workers/lib/jsonrpc-ws-uds.mjs +0 -2
  42. package/package.json +1 -1
  43. package/scripts/lib/agent-route-policy.mjs +162 -14
  44. package/scripts/lib/claudemd-scanner.mjs +2 -199
  45. package/scripts/lib/cli-claude.mjs +0 -2
  46. package/scripts/lib/context.mjs +3 -19
  47. package/scripts/lib/env-probe.mjs +5 -64
  48. package/scripts/lib/gemini-profiles.mjs +2 -69
  49. package/scripts/lib/machine-profile.mjs +3 -17
  50. package/scripts/lib/mcp-filter.mjs +109 -53
  51. package/scripts/lib/mcp-health.mjs +2 -2
  52. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  53. package/scripts/lib/psmux-info.mjs +11 -59
  54. package/scripts/lib/skill-template.mjs +0 -245
  55. package/hub/team/build-worker-prompt.mjs +0 -92
  56. package/hub/team/check-mcp-hub.mjs +0 -49
  57. package/hub/team/claude-native-bridge.mjs +0 -987
  58. package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
  59. package/hub/team/cli/commands/start/start-wt.mjs +0 -86
  60. package/hub/team/cli/services/native-control.mjs +0 -145
  61. package/hub/team/codex-review.mjs +0 -520
  62. package/hub/team/conductor-mesh-bridge.mjs +0 -122
  63. package/hub/team/conductor.mjs +0 -1529
  64. package/hub/team/consensus-meta.mjs +0 -176
  65. package/hub/team/cto-pull.mjs +0 -31
  66. package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
  67. package/hub/team/dashboard.mjs +0 -322
  68. package/hub/team/event-log.mjs +0 -84
  69. package/hub/team/execution-mode.mjs +0 -324
  70. package/hub/team/extract-completion-payload.mjs +0 -110
  71. package/hub/team/headless-bridge-session.mjs +0 -17
  72. package/hub/team/health-probe.mjs +0 -435
  73. package/hub/team/interactive-native-launcher.mjs +0 -119
  74. package/hub/team/interactive-native-worker.mjs +0 -80
  75. package/hub/team/interactive-tui-transport.mjs +0 -479
  76. package/hub/team/launcher-template.mjs +0 -144
  77. package/hub/team/mcp-selector.mjs +0 -145
  78. package/hub/team/native-supervisor.mjs +0 -460
  79. package/hub/team/native.mjs +0 -727
  80. package/hub/team/recovery-store.mjs +0 -149
  81. package/hub/team/remote-probe.mjs +0 -292
  82. package/hub/team/remote-watcher.mjs +0 -506
  83. package/hub/team/runtime-strategy.mjs +0 -200
  84. package/hub/team/schemas/worker-completion.json +0 -39
  85. package/hub/team/sentinel-capture.mjs +0 -127
  86. package/hub/team/swarm-cli.mjs +0 -390
  87. package/hub/team/swarm-hypervisor.mjs +0 -2206
  88. package/hub/team/swarm-locks.mjs +0 -335
  89. package/hub/team/swarm-planner.mjs +0 -371
  90. package/hub/team/swarm-preflight.mjs +0 -366
  91. package/hub/team/swarm-reconciler.mjs +0 -242
  92. package/hub/team/tui-remote-adapter.mjs +0 -377
  93. package/hub/team/worker-completion-validator.mjs +0 -79
  94. package/hub/team/worktree-lifecycle.mjs +0 -639
  95. package/hub/team/wt-templates.json +0 -43
  96. package/hub/workers/antigravity-route-worker.mjs +0 -179
  97. package/scripts/lib/dynamic-route-cli.mjs +0 -107
@@ -1,9 +1,12 @@
1
1
  // Canonical Codex role policy. Concrete model IDs stay in profile config files.
2
2
 
3
+ import { readFileSync } from "node:fs";
4
+ import { pathToFileURL } from "node:url";
3
5
  import {
4
6
  normalizeCodexProfileName,
5
7
  resolveCodexProfileConfigValues,
6
8
  } from "./codex-profile-config.mjs";
9
+ import { resolveGeminiProfileForPurpose } from "./gemini-profiles.mjs";
7
10
 
8
11
  const policy = {
9
12
  executor: {
@@ -198,7 +201,7 @@ function freezePolicy(entry) {
198
201
  return Object.freeze({ ...entry });
199
202
  }
200
203
 
201
- export const CODEX_AGENT_POLICY = Object.freeze(
204
+ const CODEX_AGENT_POLICY = Object.freeze(
202
205
  Object.fromEntries(
203
206
  Object.entries(policy).map(([agent, entry]) => [
204
207
  agent,
@@ -206,9 +209,9 @@ export const CODEX_AGENT_POLICY = Object.freeze(
206
209
  ]),
207
210
  ),
208
211
  );
209
- export const DEFAULT_CODEX_AGENT = "executor";
212
+ const DEFAULT_CODEX_AGENT = "executor";
210
213
 
211
- export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
214
+ const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
212
215
  new Set([
213
216
  "gpt6_luna_low",
214
217
  "gpt6_luna_high",
@@ -219,7 +222,7 @@ export const CANONICAL_CODEX_PROFILE_OVERRIDES = Object.freeze(
219
222
  "gpt6_astra_ultra",
220
223
  ]),
221
224
  );
222
- export const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
225
+ const ULTRA_ELIGIBLE_CODEX_AGENTS = Object.freeze(
223
226
  new Set(["deep-executor", "scientist-deep"]),
224
227
  );
225
228
 
@@ -316,31 +319,176 @@ export function resolveNestedCodexAgentProfile(
316
319
  return effort.toLowerCase() === "ultra" ? "gpt6_astra_max" : candidate;
317
320
  }
318
321
 
319
- export function describeCodexAgentPolicy() {
320
- return {
321
- defaultAgent: DEFAULT_CODEX_AGENT,
322
- agents: Object.keys(CODEX_AGENT_POLICY),
323
- };
322
+ const MIN_TIMEOUT_BY_AGENT = Object.freeze({
323
+ "deep-executor": 900,
324
+ architect: 900,
325
+ planner: 900,
326
+ critic: 900,
327
+ analyst: 900,
328
+ "document-specialist": 900,
329
+ scientist: 900,
330
+ "scientist-deep": 900,
331
+ "code-reviewer": 600,
332
+ "security-reviewer": 600,
333
+ "quality-reviewer": 600,
334
+ executor: 300,
335
+ debugger: 300,
336
+ });
337
+
338
+ const EXPECTED_DURATION_BY_AGENT = Object.freeze({
339
+ writer: 90,
340
+ verifier: 90,
341
+ "qa-tester": 90,
342
+ executor: 300,
343
+ debugger: 300,
344
+ "test-engineer": 300,
345
+ "code-reviewer": 600,
346
+ "security-reviewer": 600,
347
+ architect: 600,
348
+ planner: 600,
349
+ critic: 600,
350
+ analyst: 600,
351
+ scientist: 900,
352
+ "scientist-deep": 900,
353
+ "deep-executor": 900,
354
+ "document-specialist": 900,
355
+ });
356
+
357
+ const NATIVE_CODEX_AGENTS = new Set([
358
+ "explore",
359
+ "verifier",
360
+ "test-engineer",
361
+ "qa-tester",
362
+ ]);
363
+
364
+ function resolveDirectCodexRoutePolicy(agent) {
365
+ if (agent === "explore") {
366
+ return { ...CODEX_AGENT_POLICY.spark, timeoutSec: 600, mcpHint: "analyze" };
367
+ }
368
+ if (agent === "antigravity" || agent === "agy") {
369
+ return CODEX_AGENT_POLICY.designer;
370
+ }
371
+ return resolveCodexAgentPolicy(agent);
372
+ }
373
+
374
+ export function resolveAgentRoute(
375
+ agent,
376
+ map,
377
+ profileOverride = "auto",
378
+ nested = false,
379
+ geminiProfileResolver = resolveGeminiProfileForPurpose,
380
+ ) {
381
+ if (!/^[A-Za-z0-9_.-]+$/.test(agent)) {
382
+ throw new Error(`invalid agent: ${agent}`);
383
+ }
384
+ if (!Object.hasOwn(map, agent)) {
385
+ throw new Error(
386
+ `알 수 없는 에이전트 타입: ${agent}; available: ${Object.keys(map).join(", ")}`,
387
+ );
388
+ }
389
+ const provider = map[agent];
390
+ if (!["codex", "antigravity", "claude"].includes(provider)) {
391
+ throw new Error(`invalid provider for ${agent}: ${provider}`);
392
+ }
393
+ const policy = resolveDirectCodexRoutePolicy(agent);
394
+ const normalizedOverride = normalizeCodexProfileName(profileOverride);
395
+ let resolvedOverride = policy.profile;
396
+ if (normalizedOverride !== "auto") {
397
+ try {
398
+ resolvedOverride = resolveCodexAgentProfile(agent, {
399
+ profileOverride: normalizedOverride,
400
+ nested,
401
+ });
402
+ } catch {
403
+ throw new Error(
404
+ `TFX_CODEX_PROFILE 값이 유효하지 않습니다: ${profileOverride}`,
405
+ );
406
+ }
407
+ }
408
+ return [
409
+ provider,
410
+ policy.profile,
411
+ policy.timeoutSec,
412
+ policy.runMode,
413
+ policy.opusOversight,
414
+ policy.mcpHint,
415
+ policy.subcommand,
416
+ normalizedOverride,
417
+ resolvedOverride,
418
+ geminiProfileResolver(agent),
419
+ MIN_TIMEOUT_BY_AGENT[agent] ?? 120,
420
+ EXPECTED_DURATION_BY_AGENT[agent] ?? 30,
421
+ NATIVE_CODEX_AGENTS.has(agent),
422
+ ];
324
423
  }
325
424
 
326
425
  function parseCliArgs(argv) {
327
426
  const args = [...argv];
328
427
  let format = "json";
329
428
  let agent = DEFAULT_CODEX_AGENT;
429
+ let mapFile = "";
430
+ let profileOverride = "auto";
431
+ let geminiProfilesModule = "";
432
+ let nested = false;
330
433
  while (args.length > 0) {
331
434
  const flag = args.shift();
332
435
  if (flag === "--format") format = args.shift() || "";
333
436
  else if (flag === "--agent") agent = args.shift() || "";
437
+ else if (flag === "--map-file") mapFile = args.shift() || "";
438
+ else if (flag === "--profile-override")
439
+ profileOverride = args.shift() || "";
440
+ else if (flag === "--gemini-profiles-module")
441
+ geminiProfilesModule = args.shift() || "";
442
+ else if (flag === "--nested") nested = true;
334
443
  else throw new Error(`unknown argument: ${flag}`);
335
444
  }
336
- if (!new Set(["json", "tsv"]).has(format)) {
445
+ if (!new Set(["json", "tsv", "route"]).has(format)) {
337
446
  throw new Error(`unsupported format: ${format}`);
338
447
  }
339
- return { format, agent };
448
+ return {
449
+ format,
450
+ agent,
451
+ mapFile,
452
+ profileOverride,
453
+ geminiProfilesModule,
454
+ nested,
455
+ };
340
456
  }
341
457
 
342
- function runCli() {
343
- const { format, agent } = parseCliArgs(process.argv.slice(2));
458
+ async function runCli() {
459
+ const {
460
+ format,
461
+ agent,
462
+ mapFile,
463
+ profileOverride,
464
+ geminiProfilesModule,
465
+ nested,
466
+ } = parseCliArgs(process.argv.slice(2));
467
+ if (format === "route") {
468
+ if (!mapFile) throw new Error("--map-file is required for route format");
469
+ const map = JSON.parse(readFileSync(mapFile, "utf8"));
470
+ let resolver = resolveGeminiProfileForPurpose;
471
+ if (geminiProfilesModule) {
472
+ try {
473
+ const module = await import(pathToFileURL(geminiProfilesModule).href);
474
+ resolver = (agent) => {
475
+ try {
476
+ return module.resolveGeminiProfileForPurpose(agent) || "flash38";
477
+ } catch {
478
+ return "flash38";
479
+ }
480
+ };
481
+ } catch {
482
+ resolver = () => "flash38";
483
+ }
484
+ }
485
+ process.stdout.write(
486
+ resolveAgentRoute(agent, map, profileOverride, nested, resolver).join(
487
+ "\x1e",
488
+ ),
489
+ );
490
+ return;
491
+ }
344
492
  const resolved = resolveCodexAgentPolicy(agent);
345
493
  if (format === "tsv") {
346
494
  process.stdout.write(
@@ -360,7 +508,7 @@ function runCli() {
360
508
 
361
509
  if (import.meta.url === new URL(process.argv[1], "file:").href) {
362
510
  try {
363
- runCli();
511
+ await runCli();
364
512
  } catch (error) {
365
513
  process.stderr.write(`[agent-route-policy] ${error.message}\n`);
366
514
  process.exitCode = 1;
@@ -1,114 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { dirname, join, resolve } from "node:path";
4
- import { fileURLToPath } from "node:url";
5
-
6
- export const TFX_START = "<!-- TFX:START -->";
7
- export const TFX_END = "<!-- TFX:END -->";
8
- export const OMC_END = "<!-- OMC:END -->";
9
- const TFX_VERSION_RE = /<!-- TFX:VERSION:([\d.]+) -->/u;
10
-
11
- const LEGACY_PATTERNS = [
12
- /<user_cli_routing>/u,
13
- /Codex Pro 무료 기간/u,
14
- /codex exec --dangerously-bypass.*skip-git-repo-check/u,
15
- /OMC 에이전트 → CLI 매핑/u,
16
- /Spark 가드레일/u,
17
- ];
18
-
19
- const DEFAULT_TFX_TEMPLATE = [
20
- "### triflux CLI routing (managed)",
21
- "- 이 블록은 triflux setup에서 자동으로 관리됩니다.",
22
- "- 직접 수정이 필요하면 블록 바깥에 사용자 섹션을 추가하세요.",
23
- ].join("\n");
24
-
25
- const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
26
- const DEFAULT_TEMPLATE_PATH = join(
27
- SCRIPT_DIR,
28
- "..",
29
- "templates",
30
- "claudemd-tfx-section.md",
31
- );
32
-
33
- function resolveVersion(version) {
34
- if (version) return version;
35
- try {
36
- const pkgPath = join(SCRIPT_DIR, "..", "..", "package.json");
37
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
38
- return pkg.version || "0.0.0";
39
- } catch {
40
- return "0.0.0";
41
- }
42
- }
43
-
44
- function resolveTemplate(template, templatePath = DEFAULT_TEMPLATE_PATH) {
45
- if (typeof template === "string" && template.trim()) {
46
- return template.trim();
47
- }
48
- if (existsSync(templatePath)) {
49
- const raw = readFileSync(templatePath, "utf8").trim();
50
- if (raw) return raw;
51
- }
52
- return DEFAULT_TFX_TEMPLATE;
53
- }
54
-
55
- function findManagedSection(rawText) {
56
- const raw = String(rawText || "");
57
- const startIdx = raw.indexOf(TFX_START);
58
- const endMarkerIdx = raw.indexOf(TFX_END);
59
- if (startIdx === -1 || endMarkerIdx === -1 || endMarkerIdx <= startIdx) {
60
- return {
61
- found: false,
62
- content: null,
63
- version: null,
64
- startIdx: -1,
65
- endIdx: -1,
66
- };
67
- }
68
-
69
- const endIdx = endMarkerIdx + TFX_END.length;
70
- const content = raw.slice(startIdx, endIdx);
71
- const versionMatch = content.match(TFX_VERSION_RE);
72
-
73
- return {
74
- found: true,
75
- content,
76
- version: versionMatch ? versionMatch[1] : null,
77
- startIdx,
78
- endIdx,
79
- };
80
- }
81
-
82
- function detectLegacyRange(rawText) {
83
- const raw = String(rawText || "");
84
- const matches = LEGACY_PATTERNS.some((pattern) => pattern.test(raw));
85
- if (!matches) {
86
- return { found: false, startIdx: -1, endIdx: -1 };
87
- }
88
-
89
- const startTag = raw.indexOf("<user_cli_routing>");
90
- const endTag = raw.indexOf("</user_cli_routing>");
91
- if (startTag === -1 || endTag === -1 || endTag <= startTag) {
92
- return { found: true, startIdx: -1, endIdx: -1 };
93
- }
94
-
95
- let removeStart = startTag;
96
- const userOverridesComment = "<!-- USER OVERRIDES";
97
- const commentIdx = raw.lastIndexOf(userOverridesComment, startTag);
98
- if (commentIdx !== -1 && startTag - commentIdx < 200) {
99
- removeStart = commentIdx;
100
- }
101
-
102
- return {
103
- found: true,
104
- startIdx: removeStart,
105
- endIdx: endTag + "</user_cli_routing>".length,
106
- };
107
- }
108
-
109
- function normalizeSpacing(text) {
110
- return String(text || "").replace(/\n{3,}/gu, "\n\n");
111
- }
3
+ import { join, resolve } from "node:path";
112
4
 
113
5
  export function findAllClaudeMdPaths(options = {}) {
114
6
  const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
@@ -132,92 +24,3 @@ export function findAllClaudeMdPaths(options = {}) {
132
24
  }
133
25
  return paths;
134
26
  }
135
-
136
- export function writeSection(filePath, options = {}) {
137
- const absolutePath = resolve(filePath);
138
- const version = resolveVersion(options.version);
139
- const template = resolveTemplate(options.template, options.templatePath);
140
- const block = [
141
- TFX_START,
142
- `<!-- TFX:VERSION:${version} -->`,
143
- template,
144
- TFX_END,
145
- ].join("\n");
146
-
147
- if (!existsSync(absolutePath)) {
148
- writeFileSync(absolutePath, `${block}\n`, "utf8");
149
- return { action: "created", version, path: absolutePath };
150
- }
151
-
152
- const raw = readFileSync(absolutePath, "utf8");
153
- const existing = findManagedSection(raw);
154
-
155
- if (existing.found) {
156
- const nextContent = `${raw.slice(0, existing.startIdx)}${block}${raw.slice(existing.endIdx)}`;
157
- writeFileSync(absolutePath, nextContent, "utf8");
158
- return {
159
- action: "updated",
160
- oldVersion: existing.version,
161
- version,
162
- path: absolutePath,
163
- };
164
- }
165
-
166
- const omcEndIdx = raw.indexOf(OMC_END);
167
- if (omcEndIdx !== -1) {
168
- const insertAt = omcEndIdx + OMC_END.length;
169
- const before = raw.slice(0, insertAt);
170
- const after = raw.slice(insertAt);
171
- const nextContent = `${before}\n${block}${after}`;
172
- writeFileSync(absolutePath, nextContent, "utf8");
173
- return { action: "inserted_after_omc", version, path: absolutePath };
174
- }
175
-
176
- const separator = raw.endsWith("\n") ? "" : "\n";
177
- writeFileSync(absolutePath, `${raw}${separator}${block}\n`, "utf8");
178
- return { action: "appended", version, path: absolutePath };
179
- }
180
-
181
- export function migrateClaudeMd(filePath, options = {}) {
182
- const absolutePath = resolve(filePath);
183
- if (!existsSync(absolutePath)) {
184
- return { action: "no_file", removed: [], path: absolutePath };
185
- }
186
-
187
- const raw = readFileSync(absolutePath, "utf8");
188
- const existing = findManagedSection(raw);
189
- const legacy = detectLegacyRange(raw);
190
-
191
- if (existing.found && !legacy.found) {
192
- return {
193
- action: "already_managed",
194
- removed: [],
195
- version: existing.version,
196
- path: absolutePath,
197
- };
198
- }
199
-
200
- const removed = [];
201
- let nextContent = raw;
202
-
203
- if (legacy.found && legacy.startIdx !== -1 && legacy.endIdx !== -1) {
204
- nextContent = `${nextContent.slice(0, legacy.startIdx)}${nextContent.slice(legacy.endIdx)}`;
205
- removed.push("<user_cli_routing> block");
206
- }
207
-
208
- const existingAfterLegacy = findManagedSection(nextContent);
209
- if (existingAfterLegacy.found) {
210
- nextContent = `${nextContent.slice(0, existingAfterLegacy.startIdx)}${nextContent.slice(existingAfterLegacy.endIdx)}`;
211
- removed.push("old TFX block");
212
- }
213
-
214
- writeFileSync(absolutePath, normalizeSpacing(nextContent), "utf8");
215
- const writeResult = writeSection(absolutePath, options);
216
-
217
- return {
218
- action: "migrated",
219
- removed,
220
- version: writeResult.version,
221
- path: absolutePath,
222
- };
223
- }
@@ -3,8 +3,6 @@
3
3
  // 책임: Claude 는 외부 CLI 가 아닌 Claude Code Agent 도구로 dispatch 된다. plan() 은
4
4
  // command=null 을 반환하고 호출자가 Agent() 로 라우팅하도록 신호한다.
5
5
  //
6
- // 출처: scripts/tfx-route.sh route_agent() L1062-L1063 (explore|claude),
7
- // L1196-L1203 (TFX_CLI_MODE=gemini fallback), TFX_VERIFIER_OVERRIDE=claude.
8
6
  // timeoutSec은 lane 예상 소요 시간(advisory)이다. hard ceiling 집행은 Bash route가 소유한다.
9
7
 
10
8
  export const id = "claude";
@@ -5,19 +5,13 @@
5
5
  * 하나의 요청에서 발생한 모든 로그를 추적할 수 있다.
6
6
  *
7
7
  * 사용법:
8
- * import { getLogger, getCorrelationId, withRequestContext } from './lib/context.mjs';
8
+ * import { getCorrelationId, withRequestContext } from './lib/context.mjs';
9
9
  *
10
10
  * // 미들웨어에서 컨텍스트 생성
11
11
  * withRequestContext({ method: 'POST', path: '/bridge/result' }, () => {
12
- * const log = getLogger();
13
- * log.info({ agentId }, 'bridge.result_received');
12
+ * const correlationId = getCorrelationId();
14
13
  * });
15
14
  *
16
- * // 내부 함수에서 자동 상관 ID
17
- * function processResult() {
18
- * const log = getLogger();
19
- * log.info('result.processed'); // correlationId 자동 포함
20
- * }
21
15
  */
22
16
  import { AsyncLocalStorage } from "node:async_hooks";
23
17
  import { randomUUID } from "node:crypto";
@@ -25,17 +19,7 @@ import { randomUUID } from "node:crypto";
25
19
  import { logger } from "./logger.mjs";
26
20
 
27
21
  /** @type {AsyncLocalStorage<{logger: import('pino').Logger, correlationId: string}>} */
28
- export const asyncLocalStorage = new AsyncLocalStorage();
29
-
30
- /**
31
- * 현재 요청 컨텍스트의 로거를 반환한다.
32
- * 요청 컨텍스트 밖에서 호출하면 기본 로거를 반환한다.
33
- *
34
- * @returns {import('pino').Logger}
35
- */
36
- export function getLogger() {
37
- return asyncLocalStorage.getStore()?.logger || logger;
38
- }
22
+ const asyncLocalStorage = new AsyncLocalStorage();
39
23
 
40
24
  /**
41
25
  * 현재 요청의 상관 ID를 반환한다.
@@ -1,25 +1,15 @@
1
- import { execSync, spawn } from "node:child_process";
1
+ import { execSync } from "node:child_process";
2
2
  import { createHash } from "node:crypto";
3
3
  import { existsSync, readFileSync } from "node:fs";
4
4
  import { homedir } from "node:os";
5
- import { dirname, join } from "node:path";
6
- import { fileURLToPath } from "node:url";
5
+ import { join } from "node:path";
7
6
  import { resolveHubPortForContext } from "../../hub/hub-lifecycle.mjs";
8
7
  import { whichCommand, whichCommandAsync } from "@triflux/core/hub/platform.mjs";
9
8
 
10
9
  const HUB_DEFAULT_PORT = 27888;
11
- const DEFAULT_STATUS_URL = "http://127.0.0.1:27888/status";
12
- const _sab = new Int32Array(new SharedArrayBuffer(4));
13
10
  const CLI_PROBE_CACHE = new Map();
14
11
  const CLI_PROBE_PROMISES = new Map();
15
12
 
16
- const __filename = fileURLToPath(import.meta.url);
17
- const DEFAULT_PKG_ROOT = join(dirname(__filename), "..", "..");
18
-
19
- function sleepSync(ms) {
20
- Atomics.wait(_sab, 0, 0, ms);
21
- }
22
-
23
13
  function fetchHubStatus({
24
14
  execSyncFn = execSync,
25
15
  statusUrl = resolveDefaultStatusUrl(),
@@ -38,10 +28,7 @@ function fetchHubStatus({
38
28
  };
39
29
  }
40
30
 
41
- export function resolveDefaultStatusUrl(
42
- env = process.env,
43
- cwd = process.cwd(),
44
- ) {
31
+ function resolveDefaultStatusUrl(env = process.env, cwd = process.cwd()) {
45
32
  const port = resolveHubPortForContext({
46
33
  env,
47
34
  cwd,
@@ -125,7 +112,7 @@ async function resolveCliProbe(name, options = {}) {
125
112
  return toCliResult(path);
126
113
  }
127
114
 
128
- export async function checkCli(name, options = {}) {
115
+ async function checkCli(name, options = {}) {
129
116
  const cliName = normalizeCliName(name);
130
117
  if (!cliName) return { ok: false };
131
118
 
@@ -168,11 +155,6 @@ export async function probeClis(names, options = {}) {
168
155
  return mapCliResults(cliNames, results);
169
156
  }
170
157
 
171
- export function resetCliProbeCache() {
172
- CLI_PROBE_CACHE.clear();
173
- CLI_PROBE_PROMISES.clear();
174
- }
175
-
176
158
  export function detectCodexAuthState({
177
159
  homeDir = homedir(),
178
160
  existsSyncFn = existsSync,
@@ -241,16 +223,9 @@ export function detectCodexPlan(options = {}) {
241
223
  export function checkHub({
242
224
  env = process.env,
243
225
  cwd = process.cwd(),
244
- pkgRoot = DEFAULT_PKG_ROOT,
245
226
  statusUrl = resolveDefaultStatusUrl(env, cwd),
246
- restart = true,
247
227
  requestTimeoutMs = 3000,
248
- pollAttempts = 8,
249
- pollIntervalMs = 500,
250
228
  execSyncFn = execSync,
251
- spawnFn = spawn,
252
- existsSyncFn = existsSync,
253
- sleepSyncFn = sleepSync,
254
229
  } = {}) {
255
230
  const guardedStatusUrl = resolveStatusUrlForContext({ statusUrl, env, cwd });
256
231
  try {
@@ -259,41 +234,7 @@ export function checkHub({
259
234
  statusUrl: guardedStatusUrl,
260
235
  timeout: requestTimeoutMs,
261
236
  });
262
- } catch {}
263
-
264
- if (!restart) return { ok: false, state: "unreachable", restart: "disabled" };
265
-
266
- const serverPath = join(pkgRoot, "hub", "server.mjs");
267
- if (!existsSyncFn(serverPath))
268
- return { ok: false, state: "unreachable", restart: "no_server" };
269
-
270
- try {
271
- const child = spawnFn(process.execPath, [serverPath], {
272
- env: { ...env, TFX_HUB_PORT: String(new URL(guardedStatusUrl).port) },
273
- detached: true,
274
- stdio: "ignore",
275
- windowsHide: true,
276
- });
277
- child.unref();
278
237
  } catch {
279
- return { ok: false, state: "unreachable", restart: "spawn_failed" };
238
+ return { ok: false, state: "unreachable", restart: "disabled" };
280
239
  }
281
-
282
- for (let i = 0; i < pollAttempts; i++) {
283
- sleepSyncFn(pollIntervalMs);
284
- try {
285
- const status = fetchHubStatus({
286
- execSyncFn,
287
- statusUrl: guardedStatusUrl,
288
- timeout: Math.min(requestTimeoutMs, 1000),
289
- });
290
- if (status.state === "healthy") {
291
- return { ...status, restarted: true };
292
- }
293
- } catch {}
294
- }
295
-
296
- return { ok: false, state: "unreachable", restart: "timeout" };
297
240
  }
298
-
299
- export { DEFAULT_PKG_ROOT, DEFAULT_STATUS_URL };