@triflux/core 10.51.0 → 10.53.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 (61) hide show
  1. package/hub/bridge.mjs +69 -1197
  2. package/hub/cli-adapter-base.mjs +4 -104
  3. package/hub/codex-adapter.mjs +7 -116
  4. package/hub/index.mjs +3 -20
  5. package/hub/lib/process-utils.mjs +1 -33
  6. package/hub/team/claude-daemon-control.mjs +1 -2
  7. package/hub/team/claude-session-projection.mjs +5 -73
  8. package/hub/team/claude-transcript.mjs +19 -0
  9. package/hub/team/nativeProxy.mjs +759 -0
  10. package/hub/team/session-context.mjs +1 -1
  11. package/hud/constants.mjs +9 -22
  12. package/hud/hud-qos-status.mjs +16 -19
  13. package/hud/providers/codex-probe.mjs +2 -31
  14. package/hud/providers/codex.mjs +16 -58
  15. package/hud/providers/gemini.mjs +20 -0
  16. package/hud/renderers.mjs +101 -77
  17. package/hud/utils.mjs +9 -13
  18. package/package.json +3 -6
  19. package/scripts/lib/agent-route-policy.mjs +0 -67
  20. package/scripts/lib/cli-claude.mjs +0 -12
  21. package/scripts/lib/env-probe.mjs +0 -70
  22. package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
  23. package/scripts/lib/mcp-guard-engine.mjs +42 -101
  24. package/scripts/lib/session-presence.mjs +1 -99
  25. package/hub/account-broker.mjs +0 -1221
  26. package/hub/adaptive-diagnostic.mjs +0 -350
  27. package/hub/adaptive-inject.mjs +0 -224
  28. package/hub/adaptive-memory.mjs +0 -277
  29. package/hub/adaptive.mjs +0 -172
  30. package/hub/assign-callbacks.mjs +0 -170
  31. package/hub/delegator/contracts.mjs +0 -38
  32. package/hub/delegator/index.mjs +0 -14
  33. package/hub/delegator/schema/delegator-tools.schema.json +0 -250
  34. package/hub/delegator/service.mjs +0 -336
  35. package/hub/delegator/tool-definitions.mjs +0 -35
  36. package/hub/hitl.mjs +0 -202
  37. package/hub/lib/known-errors.json +0 -72
  38. package/hub/lib/memory-store.mjs +0 -1001
  39. package/hub/lib/phase-manager.mjs +0 -493
  40. package/hub/lib/state-snapshot.mjs +0 -293
  41. package/hub/lib/trace-recorder.mjs +0 -153
  42. package/hub/lib/uuidv7.mjs +0 -45
  43. package/hub/memory-doctor.mjs +0 -640
  44. package/hub/middleware/quota-middleware.mjs +0 -70
  45. package/hub/middleware/request-logger.mjs +0 -191
  46. package/hub/paths.mjs +0 -24
  47. package/hub/pipeline/index.mjs +0 -163
  48. package/hub/pipeline/state.mjs +0 -191
  49. package/hub/pipeline/transitions.mjs +0 -131
  50. package/hub/reflexion.mjs +0 -290
  51. package/hub/role-contract.mjs +0 -312
  52. package/hub/router.mjs +0 -2026
  53. package/hub/session-fingerprint.mjs +0 -418
  54. package/hub/state.mjs +0 -300
  55. package/hub/team/synapse-http.mjs +0 -311
  56. package/hub/team-bridge.mjs +0 -27
  57. package/scripts/lib/claudemd-scanner.mjs +0 -26
  58. package/scripts/lib/context.mjs +0 -51
  59. package/scripts/lib/handoff.mjs +0 -187
  60. package/scripts/lib/logger.mjs +0 -122
  61. package/scripts/lib/stealth-fetch.mjs +0 -176
@@ -1,640 +0,0 @@
1
- import {
2
- copyFileSync,
3
- existsSync,
4
- lstatSync,
5
- mkdirSync,
6
- readdirSync,
7
- readFileSync,
8
- renameSync,
9
- writeFileSync,
10
- } from "node:fs";
11
- import { basename, join, resolve } from "node:path";
12
-
13
- // ── Constants ──
14
-
15
- const SEVERITY = Object.freeze({ P0: "P0", P1: "P1", P2: "P2" });
16
- const WEIGHT = Object.freeze({ P0: 2.0, P1: 0.5, P2: 0.2 });
17
-
18
- const MEMORY_LINK_RE = /\[([^\]]*)\]\(([^)]+\.md)\)/gu;
19
-
20
- const PATHS_YAML_RE = /^paths:\s*\n(\s+-\s+.+\n?)+/mu;
21
-
22
- const RULE_VIOLATION_PATTERNS = [
23
- /^\|\s*스킬\s*\|/mu,
24
- /^\|\s*디렉토리\s*\|/mu,
25
- /^```\s*(?:tree|npm ls|git log)/mu,
26
- /^\s*├──|└──/mu,
27
- ];
28
-
29
- const FRONTMATTER_RE = /^---\n([\s\S]*?)\n---/u;
30
-
31
- const HANGUL_RE = /[\uAC00-\uD7A3]/u;
32
- const LATIN_RE = /[a-zA-Z]{3,}/u;
33
-
34
- // ── Helpers ──
35
-
36
- const clamp = (v, min, max) => Math.max(min, Math.min(max, v));
37
-
38
- function parseFrontmatter(content) {
39
- const match = FRONTMATTER_RE.exec(String(content || ""));
40
- if (!match) return { found: false, fields: {}, body: content };
41
-
42
- const fields = {};
43
- for (const line of match[1].split("\n")) {
44
- const sep = line.indexOf(":");
45
- if (sep === -1) continue;
46
- const key = line.slice(0, sep).trim();
47
- const val = line.slice(sep + 1).trim();
48
- if (key) fields[key] = val;
49
- }
50
- return {
51
- found: true,
52
- fields,
53
- body: String(content).slice(match[0].length).trim(),
54
- };
55
- }
56
-
57
- function safeReadFile(filePath) {
58
- try {
59
- return existsSync(filePath) ? readFileSync(filePath, "utf8") : null;
60
- } catch {
61
- return null;
62
- }
63
- }
64
-
65
- function safeReadDir(dirPath) {
66
- try {
67
- return existsSync(dirPath) ? readdirSync(dirPath) : [];
68
- } catch {
69
- return [];
70
- }
71
- }
72
-
73
- function isCI() {
74
- return (
75
- process.env.CI === "true" ||
76
- process.env.DOCKER === "true" ||
77
- (process.env.TFX_POSTINSTALL_FIX !== "1" && process.env.CI != null)
78
- );
79
- }
80
-
81
- function makeCheckResult(id, name, severity, autofix, issues, error = null) {
82
- return Object.freeze({
83
- id,
84
- name,
85
- severity,
86
- autofix,
87
- issues: Object.freeze([...issues]),
88
- passed: issues.length === 0 && error == null,
89
- error,
90
- });
91
- }
92
-
93
- // ── Check Functions ──
94
-
95
- function checkOrphanFiles(memoryDir) {
96
- const indexPath = join(memoryDir, "MEMORY.md");
97
- const indexContent = safeReadFile(indexPath);
98
- if (indexContent == null) {
99
- return makeCheckResult(
100
- "orphan-files",
101
- "고아 파일 감지",
102
- SEVERITY.P0,
103
- true,
104
- [],
105
- );
106
- }
107
-
108
- const indexed = new Set();
109
- let m;
110
- const re = new RegExp(MEMORY_LINK_RE.source, MEMORY_LINK_RE.flags);
111
- while ((m = re.exec(indexContent)) !== null) {
112
- indexed.add(m[2]);
113
- }
114
-
115
- const diskFiles = safeReadDir(memoryDir).filter(
116
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
117
- );
118
-
119
- const orphans = diskFiles
120
- .filter((f) => !indexed.has(f))
121
- .map((f) => ({ file: f, detail: "MEMORY.md 인덱스에 미등록" }));
122
-
123
- return makeCheckResult(
124
- "orphan-files",
125
- "고아 파일 감지",
126
- SEVERITY.P0,
127
- true,
128
- orphans,
129
- );
130
- }
131
-
132
- function checkPathsYamlBug(rulesDir) {
133
- const files = safeReadDir(rulesDir).filter((f) => f.endsWith(".md"));
134
- const issues = [];
135
-
136
- for (const file of files) {
137
- const content = safeReadFile(join(rulesDir, file));
138
- if (content == null) continue;
139
-
140
- const fm = parseFrontmatter(content);
141
- if (!fm.found) continue;
142
-
143
- const rawFm = FRONTMATTER_RE.exec(content);
144
- if (rawFm && PATHS_YAML_RE.test(rawFm[1])) {
145
- issues.push({
146
- file,
147
- detail: "paths: YAML 배열 사용 (동작 안 함 — globs: CSV로 변환 필요)",
148
- });
149
- }
150
- }
151
-
152
- return makeCheckResult(
153
- "paths-yaml-bug",
154
- "paths: YAML 배열 버그",
155
- SEVERITY.P0,
156
- true,
157
- issues,
158
- );
159
- }
160
-
161
- function checkTrifluxResidue(projectDir, claudeDir) {
162
- const issues = [];
163
-
164
- const omcState = join(projectDir, ".omc", "state");
165
- if (existsSync(omcState)) {
166
- const now = Date.now();
167
- const staleThreshold = 7 * 24 * 60 * 60 * 1000;
168
- for (const f of safeReadDir(omcState).filter((f) => f.endsWith(".json"))) {
169
- try {
170
- const stat = lstatSync(join(omcState, f));
171
- if (now - stat.mtimeMs > staleThreshold) {
172
- issues.push({
173
- file: `.omc/state/${f}`,
174
- detail: "7일 이상 된 stale state 파일",
175
- });
176
- }
177
- } catch {
178
- /* skip */
179
- }
180
- }
181
- }
182
-
183
- const scriptsDir = join(claudeDir, "scripts");
184
- for (const f of safeReadDir(scriptsDir)) {
185
- try {
186
- const fullPath = join(scriptsDir, f);
187
- const stat = lstatSync(fullPath);
188
- if (stat.isSymbolicLink() && !existsSync(fullPath)) {
189
- issues.push({
190
- file: `~/.claude/scripts/${f}`,
191
- detail: "깨진 심볼릭 링크",
192
- });
193
- }
194
- } catch {
195
- /* skip */
196
- }
197
- }
198
-
199
- return makeCheckResult(
200
- "triflux-residue",
201
- "기존 triflux 잔재",
202
- SEVERITY.P0,
203
- true,
204
- issues,
205
- );
206
- }
207
-
208
- function checkRuleViolation(memoryDir) {
209
- const files = safeReadDir(memoryDir).filter(
210
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
211
- );
212
- const issues = [];
213
-
214
- for (const file of files) {
215
- const content = safeReadFile(join(memoryDir, file));
216
- if (content == null) continue;
217
-
218
- for (const pattern of RULE_VIOLATION_PATTERNS) {
219
- if (pattern.test(content)) {
220
- issues.push({ file, detail: "코드에서 도출 가능한 정보 포함" });
221
- break;
222
- }
223
- }
224
- }
225
-
226
- return makeCheckResult(
227
- "rule-violation",
228
- "저장 규칙 위반",
229
- SEVERITY.P1,
230
- false,
231
- issues,
232
- );
233
- }
234
-
235
- function checkStaleReferences(memoryDir, projectDir) {
236
- const files = safeReadDir(memoryDir).filter(
237
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
238
- );
239
- const issues = [];
240
- const fileRefRe = /(?:^|\s)([a-zA-Z][\w/.%-]+\.[a-z]{1,5})(?::(\d+))?/gu;
241
-
242
- for (const file of files) {
243
- const content = safeReadFile(join(memoryDir, file));
244
- if (content == null) continue;
245
-
246
- let m;
247
- const re = new RegExp(fileRefRe.source, fileRefRe.flags);
248
- while ((m = re.exec(content)) !== null) {
249
- const refPath = m[1];
250
- if (
251
- refPath.startsWith("http") ||
252
- refPath.startsWith("node:") ||
253
- refPath.includes("node_modules")
254
- )
255
- continue;
256
- const fullPath = resolve(projectDir, refPath);
257
- if (!existsSync(fullPath) && refPath.includes("/")) {
258
- issues.push({ file, detail: `존재하지 않는 파일 참조: ${refPath}` });
259
- }
260
- }
261
- }
262
-
263
- return makeCheckResult(
264
- "stale-references",
265
- "stale 참조",
266
- SEVERITY.P1,
267
- false,
268
- issues,
269
- );
270
- }
271
-
272
- function checkLanguageInconsistency(memoryDir) {
273
- const files = safeReadDir(memoryDir).filter(
274
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
275
- );
276
- const issues = [];
277
-
278
- let koCount = 0;
279
- let enCount = 0;
280
-
281
- for (const file of files) {
282
- const content = safeReadFile(join(memoryDir, file));
283
- if (content == null) continue;
284
- const fm = parseFrontmatter(content);
285
- if (!fm.found) continue;
286
-
287
- const nameVal = fm.fields.name || "";
288
- const descVal = fm.fields.description || "";
289
- const combined = `${nameVal} ${descVal}`;
290
-
291
- if (HANGUL_RE.test(combined)) koCount++;
292
- else if (LATIN_RE.test(combined)) enCount++;
293
- }
294
-
295
- const majorLang = koCount >= enCount ? "ko" : "en";
296
- const isKoMajor = majorLang === "ko";
297
-
298
- for (const file of files) {
299
- const content = safeReadFile(join(memoryDir, file));
300
- if (content == null) continue;
301
- const fm = parseFrontmatter(content);
302
- if (!fm.found || !fm.fields.name) continue;
303
-
304
- const combined = `${fm.fields.name} ${fm.fields.description || ""}`;
305
- const hasKo = HANGUL_RE.test(combined);
306
- const hasEn = LATIN_RE.test(combined);
307
-
308
- if (isKoMajor && !hasKo && hasEn) {
309
- issues.push({ file, detail: `다수 언어(한국어)와 불일치 — 영어 전용` });
310
- } else if (!isKoMajor && hasKo && !hasEn) {
311
- issues.push({ file, detail: `다수 언어(영어)와 불일치 — 한국어 전용` });
312
- }
313
- }
314
-
315
- return makeCheckResult(
316
- "language-inconsistency",
317
- "언어 혼용",
318
- SEVERITY.P2,
319
- false,
320
- issues,
321
- );
322
- }
323
-
324
- function checkOversizedFiles(memoryDir) {
325
- const files = safeReadDir(memoryDir).filter(
326
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
327
- );
328
- const issues = [];
329
-
330
- for (const file of files) {
331
- const content = safeReadFile(join(memoryDir, file));
332
- if (content == null) continue;
333
- const lineCount = content.split("\n").length;
334
- if (lineCount > 50) {
335
- issues.push({ file, detail: `${lineCount}줄 (50줄 초과)` });
336
- }
337
- }
338
-
339
- return makeCheckResult(
340
- "oversized-files",
341
- "과대 파일",
342
- SEVERITY.P2,
343
- false,
344
- issues,
345
- );
346
- }
347
-
348
- function checkMissingUserMemory(memoryDir) {
349
- const files = safeReadDir(memoryDir).filter(
350
- (f) => f.endsWith(".md") && f !== "MEMORY.md",
351
- );
352
- const issues = [];
353
-
354
- const hasUserType = files.some((file) => {
355
- const content = safeReadFile(join(memoryDir, file));
356
- if (content == null) return false;
357
- const fm = parseFrontmatter(content);
358
- return fm.found && fm.fields.type === "user";
359
- });
360
-
361
- if (!hasUserType && files.length > 0) {
362
- issues.push({
363
- file: "memory/",
364
- detail: "type: user 메모리 파일 없음 — 사용자 프로필 미등록",
365
- });
366
- }
367
-
368
- return makeCheckResult(
369
- "missing-user-memory",
370
- "user 타입 메모리 부재",
371
- SEVERITY.P2,
372
- true,
373
- issues,
374
- );
375
- }
376
-
377
- // ── Fix Functions ──
378
-
379
- function createBackup(files, backupDir) {
380
- const timestamp = new Date()
381
- .toISOString()
382
- .replace(/[:.]/gu, "-")
383
- .slice(0, 19);
384
- const dir = join(backupDir, timestamp);
385
- mkdirSync(dir, { recursive: true });
386
-
387
- const backed = [];
388
- for (const src of files) {
389
- if (!existsSync(src)) continue;
390
- const dst = join(dir, basename(src));
391
- copyFileSync(src, dst);
392
- backed.push({ src, dst });
393
- }
394
-
395
- writeFileSync(
396
- join(dir, "manifest.json"),
397
- JSON.stringify(
398
- {
399
- timestamp,
400
- files: backed,
401
- restore: "tfx memory-doctor --undo",
402
- },
403
- null,
404
- 2,
405
- ),
406
- "utf8",
407
- );
408
-
409
- return { dir, files: backed };
410
- }
411
-
412
- function fixOrphanFiles(memoryDir, issues) {
413
- const indexPath = join(memoryDir, "MEMORY.md");
414
- const content = safeReadFile(indexPath);
415
- if (content == null) return { action: "skipped", fixed: [] };
416
-
417
- const additions = issues.map(({ file }) => {
418
- const fm = parseFrontmatter(safeReadFile(join(memoryDir, file)) || "");
419
- const desc = fm.fields.description || "(설명 없음)";
420
- return `- [${fm.fields.name || file}](${file}) — ${desc}`;
421
- });
422
-
423
- if (additions.length === 0) return { action: "unchanged", fixed: [] };
424
-
425
- const separator = content.endsWith("\n") ? "" : "\n";
426
- writeFileSync(
427
- indexPath,
428
- `${content}${separator}${additions.join("\n")}\n`,
429
- "utf8",
430
- );
431
-
432
- return { action: "fixed", fixed: issues.map((i) => i.file) };
433
- }
434
-
435
- function fixPathsYamlBug(rulesDir, issues) {
436
- const fixed = [];
437
-
438
- for (const { file } of issues) {
439
- const filePath = join(rulesDir, file);
440
- const content = safeReadFile(filePath);
441
- if (content == null) continue;
442
-
443
- const fmMatch = FRONTMATTER_RE.exec(content);
444
- if (!fmMatch) continue;
445
-
446
- const fmRaw = fmMatch[1];
447
- const pathsMatch = PATHS_YAML_RE.exec(fmRaw);
448
- if (!pathsMatch) continue;
449
-
450
- const values = [];
451
- for (const line of pathsMatch[0].split("\n")) {
452
- const trimmed = line
453
- .replace(/^\s*-\s*/, "")
454
- .replace(/^["']|["']$/gu, "")
455
- .trim();
456
- if (trimmed && trimmed !== "paths:") values.push(trimmed);
457
- }
458
-
459
- if (values.length === 0) continue;
460
-
461
- const globsLine = `globs: ${values.join(", ")}`;
462
- const nextFm = fmRaw.replace(pathsMatch[0], globsLine);
463
- const nextContent = `---\n${nextFm}\n---${content.slice(fmMatch[0].length)}`;
464
- writeFileSync(filePath, nextContent, "utf8");
465
- fixed.push(file);
466
- }
467
-
468
- return { action: fixed.length > 0 ? "fixed" : "unchanged", fixed };
469
- }
470
-
471
- function fixTrifluxResidue(issues) {
472
- const fixed = [];
473
-
474
- for (const { file } of issues) {
475
- try {
476
- const fullPath = resolve(
477
- file.replace(/^~\/\.claude/u, join(process.env.HOME || "", ".claude")),
478
- );
479
- if (!existsSync(fullPath)) continue;
480
-
481
- const archiveDir = join(resolve(".tfx", "archive"));
482
- mkdirSync(archiveDir, { recursive: true });
483
- renameSync(fullPath, join(archiveDir, basename(fullPath)));
484
- fixed.push(file);
485
- } catch {
486
- /* skip unresolvable */
487
- }
488
- }
489
-
490
- return { action: fixed.length > 0 ? "fixed" : "unchanged", fixed };
491
- }
492
-
493
- // ── Health Score ──
494
-
495
- function computeHealthScore(checks) {
496
- let penalty = 0;
497
- for (const check of checks) {
498
- if (check.error != null) continue;
499
- const count = check.issues.length;
500
- const w = WEIGHT[check.severity] || 0;
501
- penalty += count * w;
502
- }
503
- return clamp(Number((10 - penalty).toFixed(1)), 0, 10);
504
- }
505
-
506
- // ── Factory ──
507
-
508
- const ALL_CHECKS = [
509
- checkOrphanFiles,
510
- checkPathsYamlBug,
511
- checkTrifluxResidue,
512
- checkRuleViolation,
513
- checkStaleReferences,
514
- checkLanguageInconsistency,
515
- checkOversizedFiles,
516
- checkMissingUserMemory,
517
- ];
518
-
519
- const FIX_MAP = {
520
- "orphan-files": fixOrphanFiles,
521
- "paths-yaml-bug": fixPathsYamlBug,
522
- "triflux-residue": fixTrifluxResidue,
523
- };
524
-
525
- export function createMemoryDoctor(options = {}) {
526
- const memoryDir = resolve(options.memoryDir || "");
527
- const rulesDir = resolve(options.rulesDir || "");
528
- const projectDir = resolve(options.projectDir || process.cwd());
529
- const claudeDir = resolve(options.claudeDir || "");
530
- const backupDir = resolve(
531
- options.backupDir || join(projectDir, ".tfx", "backups"),
532
- );
533
-
534
- function scan() {
535
- const checks = ALL_CHECKS.map((fn) => {
536
- try {
537
- if (fn === checkOrphanFiles) return fn(memoryDir);
538
- if (fn === checkPathsYamlBug) return fn(rulesDir);
539
- if (fn === checkTrifluxResidue) return fn(projectDir, claudeDir);
540
- if (fn === checkRuleViolation) return fn(memoryDir);
541
- if (fn === checkStaleReferences) return fn(memoryDir, projectDir);
542
- if (fn === checkLanguageInconsistency) return fn(memoryDir);
543
- if (fn === checkOversizedFiles) return fn(memoryDir);
544
- if (fn === checkMissingUserMemory) return fn(memoryDir);
545
- return fn(memoryDir);
546
- } catch (err) {
547
- return makeCheckResult(
548
- fn.name
549
- .replace("check", "")
550
- .replace(/([A-Z])/gu, "-$1")
551
- .toLowerCase()
552
- .replace(/^-/u, ""),
553
- fn.name,
554
- SEVERITY.P2,
555
- false,
556
- [],
557
- err.message,
558
- );
559
- }
560
- });
561
-
562
- const healthScore = computeHealthScore(checks);
563
- const summary = {
564
- p0: checks.filter((c) => c.severity === SEVERITY.P0 && !c.passed).length,
565
- p1: checks.filter((c) => c.severity === SEVERITY.P1 && !c.passed).length,
566
- p2: checks.filter((c) => c.severity === SEVERITY.P2 && !c.passed).length,
567
- };
568
-
569
- return Object.freeze({ checks, healthScore, summary });
570
- }
571
-
572
- function fix(checkId, opts = {}) {
573
- if (isCI() && !opts.force) {
574
- return { action: "skipped_ci", backup: null, fixed: [] };
575
- }
576
-
577
- const { checks } = scan();
578
- const check = checks.find((c) => c.id === checkId);
579
- if (!check || check.passed)
580
- return { action: "no_issues", backup: null, fixed: [] };
581
- if (!check.autofix)
582
- return { action: "manual_only", backup: null, fixed: [] };
583
-
584
- const fixFn = FIX_MAP[checkId];
585
- if (!fixFn) return { action: "no_fixer", backup: null, fixed: [] };
586
-
587
- const affectedFiles = check.issues
588
- .map((i) => {
589
- const candidate = join(
590
- checkId === "orphan-files" ? memoryDir : rulesDir,
591
- i.file,
592
- );
593
- return existsSync(candidate) ? candidate : null;
594
- })
595
- .filter(Boolean);
596
-
597
- const indexFile =
598
- checkId === "orphan-files" ? join(memoryDir, "MEMORY.md") : null;
599
- const backupFiles = indexFile
600
- ? [indexFile, ...affectedFiles]
601
- : affectedFiles;
602
-
603
- const backup =
604
- backupFiles.length > 0 ? createBackup(backupFiles, backupDir) : null;
605
-
606
- const dir =
607
- checkId === "orphan-files"
608
- ? memoryDir
609
- : checkId === "paths-yaml-bug"
610
- ? rulesDir
611
- : projectDir;
612
- const result = fixFn(dir, check.issues);
613
-
614
- return { ...result, backup: backup?.dir || null };
615
- }
616
-
617
- function fixAll(opts = {}) {
618
- if (isCI() && !opts.force) {
619
- return { results: [], backup: null };
620
- }
621
-
622
- const { checks } = scan();
623
- const targets = checks.filter((c) => {
624
- if (opts.severity && c.severity !== opts.severity) return false;
625
- return c.autofix && !c.passed;
626
- });
627
-
628
- const results = targets.map((c) => ({ id: c.id, ...fix(c.id, opts) }));
629
- return { results };
630
- }
631
-
632
- return Object.freeze({
633
- scan,
634
- fix,
635
- fixAll,
636
- getHealthScore: computeHealthScore,
637
- });
638
- }
639
-
640
- export { computeHealthScore, isCI, parseFrontmatter };
@@ -1,70 +0,0 @@
1
- const FAILURE_STATUSES = new Set(["quota_hit", "error"]);
2
-
3
- function buildFailureRecord(result = {}) {
4
- const failure = {
5
- id: result.id ?? "unknown",
6
- status: result.status ?? "unknown",
7
- };
8
-
9
- if (Number.isFinite(result.http)) {
10
- failure.http = result.http;
11
- }
12
-
13
- if (typeof result.message === "string" && result.message.length > 0) {
14
- failure.message = result.message;
15
- }
16
-
17
- if (result.headers && Object.keys(result.headers).length > 0) {
18
- failure.headers = result.headers;
19
- }
20
-
21
- return failure;
22
- }
23
-
24
- export function summarizeQuotaResults(results = []) {
25
- const metrics = {
26
- checked: results.length,
27
- ok: 0,
28
- quotaHit: 0,
29
- error: 0,
30
- failed: 0,
31
- };
32
- const failures = [];
33
-
34
- for (const result of results) {
35
- const status = result?.status ?? "unknown";
36
-
37
- if (status === "ok") {
38
- metrics.ok += 1;
39
- } else if (status === "quota_hit") {
40
- metrics.quotaHit += 1;
41
- } else if (status === "error") {
42
- metrics.error += 1;
43
- }
44
-
45
- if (FAILURE_STATUSES.has(status)) {
46
- failures.push(buildFailureRecord(result));
47
- }
48
- }
49
-
50
- metrics.failed = failures.length;
51
- return { metrics, failures };
52
- }
53
-
54
- export function logQuotaRefreshFailures(logger, results = []) {
55
- const { metrics, failures } = summarizeQuotaResults(results);
56
- if (failures.length === 0) {
57
- return { logged: false, metrics, failures };
58
- }
59
-
60
- logger.warn(
61
- {
62
- tag: "hub-quota",
63
- metrics,
64
- failures,
65
- },
66
- "broker.quota_refresh_degraded",
67
- );
68
-
69
- return { logged: true, metrics, failures };
70
- }