@triflux/core 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 (46) hide show
  1. package/hooks/agy-session-hook.mjs +11 -56
  2. package/hooks/codex-session-hook.mjs +11 -101
  3. package/hub/bridge.mjs +1 -79
  4. package/hub/cli-adapter-base.mjs +12 -90
  5. package/hub/codex-adapter.mjs +0 -1
  6. package/hub/codex-preflight.mjs +5 -28
  7. package/hub/index.mjs +1 -1
  8. package/hub/lib/known-errors.json +2 -2
  9. package/hub/lib/process-utils.mjs +0 -123
  10. package/hub/lib/spawn-trace.mjs +3 -2
  11. package/hub/lib/timeout-defaults.mjs +0 -1
  12. package/hub/role-contract.mjs +0 -2
  13. package/hud/cli-policy.mjs +0 -22
  14. package/hud/constants.mjs +11 -26
  15. package/hud/hud-qos-status.mjs +23 -51
  16. package/hud/providers/gemini.mjs +105 -20
  17. package/hud/renderers.mjs +50 -104
  18. package/hud/utils.mjs +2 -2
  19. package/package.json +1 -3
  20. package/scripts/lib/agent-route-policy.mjs +162 -14
  21. package/scripts/lib/claudemd-scanner.mjs +2 -199
  22. package/scripts/lib/cli-claude.mjs +0 -2
  23. package/scripts/lib/context.mjs +3 -19
  24. package/scripts/lib/env-probe.mjs +5 -64
  25. package/scripts/lib/gemini-profiles.mjs +2 -69
  26. package/scripts/lib/machine-profile.mjs +3 -17
  27. package/scripts/lib/mcp-filter.mjs +109 -53
  28. package/scripts/lib/mcp-health.mjs +2 -2
  29. package/scripts/lib/mcp-server-catalog.mjs +2 -2
  30. package/scripts/lib/psmux-info.mjs +11 -59
  31. package/scripts/lib/session-presence.mjs +1 -1
  32. package/scripts/lib/skill-template.mjs +0 -245
  33. package/hub/dynamic-routing-engine.mjs +0 -511
  34. package/hub/lib/risk-tier.mjs +0 -53
  35. package/hub/lib/ssh-command.mjs +0 -231
  36. package/hub/lib/staged-file-detect.mjs +0 -43
  37. package/hub/lib/tfx-route-args.mjs +0 -353
  38. package/hub/routing-snapshot.mjs +0 -261
  39. package/mesh/index.mjs +0 -69
  40. package/mesh/mesh-budget.mjs +0 -137
  41. package/mesh/mesh-heartbeat.mjs +0 -100
  42. package/mesh/mesh-protocol.mjs +0 -96
  43. package/mesh/mesh-queue.mjs +0 -163
  44. package/mesh/mesh-registry.mjs +0 -78
  45. package/mesh/mesh-router.mjs +0 -77
  46. package/scripts/lib/dynamic-route-cli.mjs +0 -107
@@ -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 "../../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 };
@@ -30,36 +30,10 @@ const DEFAULT_GEMINI_PROFILES = {
30
30
  },
31
31
  };
32
32
 
33
- // 1회 마이그레이션 테이블: 옛 ID 형식 모델값 → agy display name.
34
- // null = 카탈로그에서 빠진 모델(2.5 계열, 3 Flash) → 해당 프로필 제거 대상.
35
- const LEGACY_MODEL_MIGRATION = {
36
- "gemini-3.1-pro-preview": "Gemini 3.1 Pro (High)",
37
- "gemini-3-flash-preview": null,
38
- "Gemini 3 Flash": null,
39
- "gemini-2.5-pro": null,
40
- "gemini-2.5-flash": null,
41
- "gemini-2.5-flash-lite": null,
42
- };
43
- // 과거 setup 이 자동 생성했던 프로필 이름. 정책상 더 쓰지 않으므로 제거한다.
44
- // 사용자가 직접 이름 붙인 프로필은 건드리지 않는다.
45
- const LEGACY_PROFILE_NAMES = [
46
- "pro25",
47
- "flash25",
48
- "lite25",
49
- "flash3",
50
- "pro31",
51
- "pro31_low",
52
- "flash35",
53
- "flash35_high",
54
- "flash35_low",
55
- ];
56
- // 과거 setup 이 자동 생성한 기본 model 값. 사용자가 고른 값이 아니므로 새 기본으로 올린다.
57
- const LEGACY_DEFAULT_MODELS = new Set(["Gemini 3.5 Flash (Medium)"]);
58
-
59
33
  // ── 용도별 effort (SSOT) ──
60
34
  // 한 세대(3.8 Flash)만 쓰고 역할에 따라 effort 를 나눈다. 키는 tfx 에이전트/역할 이름
61
35
  // (agent-map.json 의 에이전트, tfx multi --assign 의 role). bash(tfx-route.sh)와
62
- // 실행 모드와 cli-agy가 이 표를 공유한다.
36
+ // 실행 모드(execution-mode)가 이 표를 읽는다.
63
37
  // High : 판단이 결과를 좌우하는 역할 (검토, 설계, 디버깅, 분석)
64
38
  // Medium : 생성/작성/실행 (기본값)
65
39
  // Low : 분류, 요약, 추출, 번역처럼 짧고 기계적인 작업
@@ -182,40 +156,6 @@ function ensureGeminiProfiles({
182
156
  )
183
157
  cfg.profiles = {};
184
158
 
185
- // ── 1회 마이그레이션: deprecated 2.5 프로필 prune + 옛 ID 형식 → display name ──
186
- // merge-only 로직만으로는 기존 사용자 파일에 남은 stale 프로필/옛 모델 ID 가
187
- // 정리되지 않으므로, 신규 default 를 채우기 전에 마이그레이션을 먼저 적용한다.
188
- let migrated = false;
189
- for (const legacy of LEGACY_PROFILE_NAMES) {
190
- if (cfg.profiles[legacy]) {
191
- delete cfg.profiles[legacy];
192
- migrated = true;
193
- }
194
- }
195
- for (const [pname, pval] of Object.entries(cfg.profiles)) {
196
- const mid = typeof pval === "string" ? pval : pval?.model;
197
- if (mid && Object.hasOwn(LEGACY_MODEL_MIGRATION, mid)) {
198
- const repl = LEGACY_MODEL_MIGRATION[mid];
199
- if (repl === null) {
200
- delete cfg.profiles[pname];
201
- } else if (typeof pval === "string") {
202
- cfg.profiles[pname] = repl;
203
- } else {
204
- cfg.profiles[pname].model = repl;
205
- }
206
- migrated = true;
207
- }
208
- }
209
- if (
210
- typeof cfg.model === "string" &&
211
- (Object.hasOwn(LEGACY_MODEL_MIGRATION, cfg.model) ||
212
- LEGACY_DEFAULT_MODELS.has(cfg.model))
213
- ) {
214
- // 옛 ID 형식/이전 세대 자동 생성 기본값은 새 기본(DEFAULT)으로 정규화한다.
215
- cfg.model = DEFAULT_GEMINI_PROFILES.model;
216
- migrated = true;
217
- }
218
-
219
159
  let added = 0;
220
160
  for (const [name, value] of Object.entries(
221
161
  DEFAULT_GEMINI_PROFILES.profiles,
@@ -227,12 +167,7 @@ function ensureGeminiProfiles({
227
167
  }
228
168
  if (!cfg.model) cfg.model = DEFAULT_GEMINI_PROFILES.model;
229
169
 
230
- if (added > 0 || migrated) {
231
- if (migrated) {
232
- try {
233
- copyFileSync(profilesPath, profilesPath + `.bak.${Date.now()}`);
234
- } catch {}
235
- }
170
+ if (added > 0) {
236
171
  writeFileSync(profilesPath, JSON.stringify(cfg, null, 2) + "\n", {
237
172
  encoding: "utf8",
238
173
  mode: 0o600,
@@ -260,8 +195,6 @@ function ensureGeminiProfiles({
260
195
  export {
261
196
  DEFAULT_GEMINI_PROFILES,
262
197
  ensureGeminiProfiles,
263
- GEMINI_DEFAULT_PURPOSE_PROFILE,
264
- GEMINI_PROFILE_BY_PURPOSE,
265
198
  resolveGeminiModel,
266
199
  resolveGeminiProfileForPurpose,
267
200
  };
@@ -14,7 +14,7 @@ import { readFileSync } from "node:fs";
14
14
  import { homedir } from "node:os";
15
15
  import { join, resolve } from "node:path";
16
16
 
17
- export const MACHINE_PROFILE_FILENAME = "machine-profile.env";
17
+ const MACHINE_PROFILE_FILENAME = "machine-profile.env";
18
18
 
19
19
  export const MACHINE_PROFILE_KEYS = Object.freeze([
20
20
  "TFX_MACHINE_PROFILE_VERSION",
@@ -28,9 +28,9 @@ export const MACHINE_PROFILE_KEYS = Object.freeze([
28
28
  "TFX_STALL_KILL",
29
29
  ]);
30
30
 
31
- export const MACHINE_PROFILE_KEY_SET = new Set(MACHINE_PROFILE_KEYS);
31
+ const MACHINE_PROFILE_KEY_SET = new Set(MACHINE_PROFILE_KEYS);
32
32
 
33
- export const MACHINE_PROFILE_VALUE_RE = /^[A-Za-z0-9_.-]+$/u;
33
+ const MACHINE_PROFILE_VALUE_RE = /^[A-Za-z0-9_.-]+$/u;
34
34
 
35
35
  // CLI 이름과 disable 키의 대응. 이 두 개가 TFX_DISABLE_* SSOT 의 전부다.
36
36
  const CLI_DISABLE_KEYS = Object.freeze({
@@ -248,20 +248,6 @@ export function normalizeCliName(cli) {
248
248
  return CLI_ALIASES[key] || null;
249
249
  }
250
250
 
251
- /**
252
- * 세션 문맥 주입용 한 줄. 형식은 고정이며 소비자가 파싱하지 않고 그대로 보여준다.
253
- *
254
- * @param {ReturnType<typeof resolveCliPolicy>} policy
255
- * @returns {string}
256
- */
257
- export function formatCliPolicyLine(policy) {
258
- const codex = policy?.codexDisabled ? "off" : "on";
259
- const antigravity = policy?.antigravityDisabled ? "off" : "on";
260
- const codexSource = policy?.source?.codex || "default";
261
- const antigravitySource = policy?.source?.antigravity || "default";
262
- return `cli-policy: codex=${codex} antigravity=${antigravity} (SSOT: TFX_DISABLE_*; codex<-${codexSource}, antigravity<-${antigravitySource})`;
263
- }
264
-
265
251
  /**
266
252
  * 차단된 CLI 실행 요청에 쓸 오류를 만든다. throw 는 호출자가 한다. 정책상 허용된
267
253
  * CLI 이거나 모르는 이름이면 null 을 준다.