@triflux/core 10.51.0 → 10.52.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.
- package/hub/bridge.mjs +65 -1172
- package/hub/index.mjs +2 -18
- package/hub/lib/process-utils.mjs +1 -33
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/nativeProxy.mjs +759 -0
- package/hub/team/session-context.mjs +1 -1
- package/hud/constants.mjs +2 -0
- package/hud/hud-qos-status.mjs +21 -5
- package/hud/providers/gemini.mjs +20 -0
- package/hud/renderers.mjs +79 -46
- package/hud/utils.mjs +9 -5
- package/package.json +2 -4
- package/scripts/lib/agent-route-policy.mjs +0 -67
- package/scripts/lib/cli-claude.mjs +0 -12
- package/scripts/lib/env-probe.mjs +0 -70
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/session-presence.mjs +1 -99
- package/hub/adaptive-diagnostic.mjs +0 -350
- package/hub/adaptive-inject.mjs +0 -224
- package/hub/adaptive-memory.mjs +0 -277
- package/hub/adaptive.mjs +0 -172
- package/hub/assign-callbacks.mjs +0 -170
- package/hub/delegator/contracts.mjs +0 -38
- package/hub/delegator/index.mjs +0 -14
- package/hub/delegator/schema/delegator-tools.schema.json +0 -250
- package/hub/delegator/service.mjs +0 -336
- package/hub/delegator/tool-definitions.mjs +0 -35
- package/hub/hitl.mjs +0 -202
- package/hub/lib/known-errors.json +0 -72
- package/hub/lib/memory-store.mjs +0 -1001
- package/hub/lib/phase-manager.mjs +0 -493
- package/hub/lib/state-snapshot.mjs +0 -293
- package/hub/lib/trace-recorder.mjs +0 -153
- package/hub/lib/uuidv7.mjs +0 -45
- package/hub/memory-doctor.mjs +0 -640
- package/hub/middleware/quota-middleware.mjs +0 -70
- package/hub/middleware/request-logger.mjs +0 -191
- package/hub/paths.mjs +0 -24
- package/hub/pipeline/index.mjs +0 -163
- package/hub/pipeline/state.mjs +0 -191
- package/hub/pipeline/transitions.mjs +0 -131
- package/hub/reflexion.mjs +0 -290
- package/hub/role-contract.mjs +0 -312
- package/hub/router.mjs +0 -2026
- package/hub/session-fingerprint.mjs +0 -418
- package/hub/state.mjs +0 -300
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team-bridge.mjs +0 -27
- package/scripts/lib/claudemd-scanner.mjs +0 -26
- package/scripts/lib/context.mjs +0 -51
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
package/scripts/lib/logger.mjs
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Logify — triflux 구조화 로깅 설정
|
|
3
|
-
*
|
|
4
|
-
* 사용법:
|
|
5
|
-
* import { logger, createModuleLogger } from './lib/logger.mjs';
|
|
6
|
-
*
|
|
7
|
-
* // 기본 로거
|
|
8
|
-
* logger.info({ taskId: 'abc' }, 'task.started');
|
|
9
|
-
*
|
|
10
|
-
* // 모듈별 로거
|
|
11
|
-
* const log = createModuleLogger('hub');
|
|
12
|
-
* log.info({ port: 27888 }, 'server.started');
|
|
13
|
-
* log.error({ err }, 'server.error');
|
|
14
|
-
*
|
|
15
|
-
* 이벤트 네이밍: {도메인}.{액션} 형식
|
|
16
|
-
* hub.started, hub.stopped, route.started, route.completed,
|
|
17
|
-
* worker.spawned, worker.completed, worker.timeout,
|
|
18
|
-
* mcp.connected, mcp.disconnected, mcp.error,
|
|
19
|
-
* team.created, team.deleted, task.claimed, task.completed,
|
|
20
|
-
* pipe.connected, pipe.message, pipe.error,
|
|
21
|
-
* http.request, http.response, http.error
|
|
22
|
-
*
|
|
23
|
-
* 로그 레벨 가이드:
|
|
24
|
-
* debug — 개발/트러블슈팅용 (변수 값, MCP 메시지, 캐시 키)
|
|
25
|
-
* info — 정상 흐름 상태 변경 (서버 시작, 워커 완료, 팀 생성)
|
|
26
|
-
* warn — 위험 신호 (재시도 발생, 쿼타 임박, 느린 워커)
|
|
27
|
-
* error — 작업 실패 (CLI 실행 실패, MCP 연결 끊김)
|
|
28
|
-
* fatal — 프로세스 위협 (DB 연결 불가, 포트 충돌)
|
|
29
|
-
*/
|
|
30
|
-
import pino from "pino";
|
|
31
|
-
|
|
32
|
-
const isDev = process.env.NODE_ENV !== "production";
|
|
33
|
-
|
|
34
|
-
// 로그는 항상 stderr(fd 2)로 보낸다.
|
|
35
|
-
//
|
|
36
|
-
// Claude Code 훅은 stdout 을 훅의 JSON 페이로드로 파싱한다. 로그가 stdout 으로
|
|
37
|
-
// 나가면 페이로드와 한 스트림에 섞여, 훅이 돌려준 hookSpecificOutput 이 JSON 으로
|
|
38
|
-
// 해석되지 않고 그냥 본문 텍스트가 된다. Claude Code 2.1.257 부터는 stdout 이
|
|
39
|
-
// "{" 로 시작하는데 유효한 JSON 이 아니면 훅 오류로 보고하므로, 로그가 먼저 나가느냐
|
|
40
|
-
// 페이로드가 먼저 나가느냐에 따라 훅 전체가 실패로 뒤집힌다.
|
|
41
|
-
//
|
|
42
|
-
// stderr 로 보내면 Claude Code 가 verbose/transcript 에서 그대로 보여주고 stdout 은
|
|
43
|
-
// 페이로드만 남는다. CLI 관례상으로도 구조화 로그는 stderr 가 맞다.
|
|
44
|
-
const LOG_FD = 2;
|
|
45
|
-
|
|
46
|
-
export const logger = pino(
|
|
47
|
-
{
|
|
48
|
-
level: process.env.LOG_LEVEL || (isDev ? "debug" : "info"),
|
|
49
|
-
|
|
50
|
-
// 모든 로그에 포함되는 기본 필드
|
|
51
|
-
base: {
|
|
52
|
-
service: process.env.SERVICE_NAME || "triflux",
|
|
53
|
-
env: process.env.NODE_ENV || "development",
|
|
54
|
-
},
|
|
55
|
-
|
|
56
|
-
// 레벨을 대문자로 출력 (AI 파싱 용이)
|
|
57
|
-
formatters: {
|
|
58
|
-
level: (label) => ({ level: label.toUpperCase() }),
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
// ISO 8601 타임스탬프
|
|
62
|
-
timestamp: pino.stdTimeFunctions.isoTime,
|
|
63
|
-
|
|
64
|
-
// 민감정보 자동 필터링
|
|
65
|
-
redact: {
|
|
66
|
-
paths: [
|
|
67
|
-
"password",
|
|
68
|
-
"token",
|
|
69
|
-
"apiKey",
|
|
70
|
-
"secret",
|
|
71
|
-
"authorization",
|
|
72
|
-
"*.password",
|
|
73
|
-
"*.token",
|
|
74
|
-
"*.apiKey",
|
|
75
|
-
"*.secret",
|
|
76
|
-
"req.headers.authorization",
|
|
77
|
-
"req.headers.cookie",
|
|
78
|
-
"hubToken",
|
|
79
|
-
],
|
|
80
|
-
remove: true,
|
|
81
|
-
},
|
|
82
|
-
|
|
83
|
-
// 개발 환경: 컬러 콘솔 출력 (stderr)
|
|
84
|
-
transport: isDev
|
|
85
|
-
? {
|
|
86
|
-
target: "pino-pretty",
|
|
87
|
-
options: {
|
|
88
|
-
colorize: true,
|
|
89
|
-
translateTime: "yyyy-mm-dd HH:MM:ss",
|
|
90
|
-
ignore: "pid,hostname",
|
|
91
|
-
destination: LOG_FD,
|
|
92
|
-
},
|
|
93
|
-
}
|
|
94
|
-
: undefined,
|
|
95
|
-
},
|
|
96
|
-
// 운영 환경(transport 미사용)에서도 stdout 이 아니라 stderr 로 쓴다.
|
|
97
|
-
isDev ? undefined : pino.destination({ dest: LOG_FD, sync: false }),
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* 모듈별 Child Logger 생성.
|
|
102
|
-
* 모듈 이름이 모든 로그에 자동 포함된다.
|
|
103
|
-
*
|
|
104
|
-
* @param {string} module — 모듈 이름 (hub, route, worker, mcp, team 등)
|
|
105
|
-
* @returns {import('pino').Logger}
|
|
106
|
-
*/
|
|
107
|
-
export function createModuleLogger(module) {
|
|
108
|
-
return logger.child({ module });
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// 정상 종료 시 버퍼 flush 보장
|
|
112
|
-
process.on("uncaughtException", (err) => {
|
|
113
|
-
logger.fatal({ err }, "process.uncaught_exception");
|
|
114
|
-
logger.flush();
|
|
115
|
-
process.exit(1);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
process.on("unhandledRejection", (reason) => {
|
|
119
|
-
logger.fatal({ reason: String(reason) }, "process.unhandled_rejection");
|
|
120
|
-
logger.flush();
|
|
121
|
-
process.exit(1);
|
|
122
|
-
});
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// SSRF boundary: stealthFetch only accepts http: and https: URLs. It does not
|
|
3
|
-
// guard private IPs, metadata hosts such as 169.254.x.x, or localhost; callers
|
|
4
|
-
// must treat the URL as already inside their trust boundary.
|
|
5
|
-
|
|
6
|
-
import { pathToFileURL } from "node:url";
|
|
7
|
-
|
|
8
|
-
export class CloakBrowserUnavailableError extends Error {}
|
|
9
|
-
|
|
10
|
-
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
11
|
-
const DEFAULT_WAIT_UNTIL = "load";
|
|
12
|
-
const SUPPORTED_PLATFORMS = new Set([
|
|
13
|
-
"linux:x64",
|
|
14
|
-
"linux:arm64",
|
|
15
|
-
"darwin:x64",
|
|
16
|
-
"darwin:arm64",
|
|
17
|
-
"win32:x64",
|
|
18
|
-
]);
|
|
19
|
-
const EXIT_CODES = {
|
|
20
|
-
not_installed: 3,
|
|
21
|
-
unsupported_platform: 4,
|
|
22
|
-
runtime_error: 5,
|
|
23
|
-
blocked_scheme: 6,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function isSupportedPlatform(platform, arch) {
|
|
27
|
-
return SUPPORTED_PLATFORMS.has(`${platform}:${arch}`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function isModuleNotFound(error) {
|
|
31
|
-
return (
|
|
32
|
-
error?.code === "ERR_MODULE_NOT_FOUND" || error?.code === "MODULE_NOT_FOUND"
|
|
33
|
-
);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function htmlToText(html) {
|
|
37
|
-
return String(html || "")
|
|
38
|
-
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/giu, " ")
|
|
39
|
-
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/giu, " ")
|
|
40
|
-
.replace(/<[^>]+>/gu, " ")
|
|
41
|
-
.replace(/ /giu, " ")
|
|
42
|
-
.replace(/&/giu, "&")
|
|
43
|
-
.replace(/</giu, "<")
|
|
44
|
-
.replace(/>/giu, ">")
|
|
45
|
-
.replace(/"/giu, '"')
|
|
46
|
-
.replace(/'/gu, "'")
|
|
47
|
-
.replace(/\s+/gu, " ")
|
|
48
|
-
.trim();
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function responseStatus(response) {
|
|
52
|
-
if (!response) return null;
|
|
53
|
-
if (typeof response.status === "function") return response.status();
|
|
54
|
-
return response.status ?? null;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function responseUrl(response, fallbackUrl) {
|
|
58
|
-
if (!response) return fallbackUrl;
|
|
59
|
-
if (typeof response.url === "function") return response.url();
|
|
60
|
-
return response.url ?? fallbackUrl;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function parseAllowedFetchUrl(url) {
|
|
64
|
-
let parsed;
|
|
65
|
-
try {
|
|
66
|
-
parsed = new URL(url);
|
|
67
|
-
} catch {
|
|
68
|
-
return { ok: false, scheme: null };
|
|
69
|
-
}
|
|
70
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
71
|
-
return { ok: false, scheme: parsed.protocol };
|
|
72
|
-
}
|
|
73
|
-
return { ok: true, href: parsed.href };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function closeBrowser(browser) {
|
|
77
|
-
if (!browser || typeof browser.close !== "function") return;
|
|
78
|
-
try {
|
|
79
|
-
await browser.close();
|
|
80
|
-
} catch {
|
|
81
|
-
// best effort: fetch callers only need the primary result signal
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export async function stealthFetch(url, opts = {}) {
|
|
86
|
-
const timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
87
|
-
const waitUntil = opts.waitUntil ?? DEFAULT_WAIT_UNTIL;
|
|
88
|
-
const loadCloakBrowser = opts._import ?? (() => import("cloakbrowser"));
|
|
89
|
-
const platform = opts._platform ?? process.platform;
|
|
90
|
-
const arch = opts._arch ?? process.arch;
|
|
91
|
-
|
|
92
|
-
if (!isSupportedPlatform(platform, arch)) {
|
|
93
|
-
return {
|
|
94
|
-
ok: false,
|
|
95
|
-
reason: "unsupported_platform",
|
|
96
|
-
platform,
|
|
97
|
-
arch,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
const parsedUrl = parseAllowedFetchUrl(url);
|
|
102
|
-
if (!parsedUrl.ok) {
|
|
103
|
-
return {
|
|
104
|
-
ok: false,
|
|
105
|
-
reason: "blocked_scheme",
|
|
106
|
-
scheme: parsedUrl.scheme,
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
let cloakbrowser;
|
|
111
|
-
try {
|
|
112
|
-
cloakbrowser = await loadCloakBrowser();
|
|
113
|
-
} catch (error) {
|
|
114
|
-
if (isModuleNotFound(error)) return { ok: false, reason: "not_installed" };
|
|
115
|
-
return { ok: false, reason: "runtime_error", error: String(error) };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
let browser;
|
|
119
|
-
try {
|
|
120
|
-
const launch = cloakbrowser?.launch;
|
|
121
|
-
if (typeof launch !== "function") {
|
|
122
|
-
throw new CloakBrowserUnavailableError("cloakbrowser launch unavailable");
|
|
123
|
-
}
|
|
124
|
-
browser = await launch();
|
|
125
|
-
const page = await browser.newPage();
|
|
126
|
-
const response = await page.goto(parsedUrl.href, {
|
|
127
|
-
waitUntil,
|
|
128
|
-
timeout: timeoutMs,
|
|
129
|
-
});
|
|
130
|
-
const html = await page.content();
|
|
131
|
-
return {
|
|
132
|
-
ok: true,
|
|
133
|
-
engine: "cloakbrowser",
|
|
134
|
-
url: parsedUrl.href,
|
|
135
|
-
finalUrl: responseUrl(response, parsedUrl.href),
|
|
136
|
-
status: responseStatus(response),
|
|
137
|
-
html,
|
|
138
|
-
text: htmlToText(html),
|
|
139
|
-
};
|
|
140
|
-
} catch (error) {
|
|
141
|
-
return { ok: false, reason: "runtime_error", error: String(error) };
|
|
142
|
-
} finally {
|
|
143
|
-
await closeBrowser(browser);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function exitCodeFor(result) {
|
|
148
|
-
return EXIT_CODES[result?.reason] ?? 5;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
export async function main(argv = process.argv) {
|
|
152
|
-
const url = argv[2];
|
|
153
|
-
if (!url) {
|
|
154
|
-
console.error("usage: stealth-fetch <url>");
|
|
155
|
-
process.exitCode = 2;
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const result = await stealthFetch(url);
|
|
160
|
-
if (result.ok) {
|
|
161
|
-
console.log(JSON.stringify(result));
|
|
162
|
-
process.exitCode = 0;
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
console.error(`[stealth-fetch] 폴백: ${result.reason}`);
|
|
167
|
-
console.log(JSON.stringify(result));
|
|
168
|
-
process.exitCode = exitCodeFor(result);
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
process.argv[1] &&
|
|
173
|
-
import.meta.url === pathToFileURL(process.argv[1]).href
|
|
174
|
-
) {
|
|
175
|
-
await main();
|
|
176
|
-
}
|