@triflux/remote 10.53.0 → 10.55.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/team/backend.mjs +10 -3
- package/hub/team/cli/commands/start/index.mjs +0 -2
- package/hub/team/handoff.mjs +1 -1
- package/hub/team/headless.mjs +41 -57
- package/hub/team/intervention.mjs +6 -4
- package/hub/team/nativeProxy.mjs +2 -48
- package/hub/team/pane.mjs +31 -18
- package/hub/team/psmux.mjs +36 -20
- package/hub/team/remote-session.mjs +66 -56
- package/hub/team/retry-state-machine.mjs +1 -1
- package/hub/team/session.mjs +80 -59
- package/hub/team/terminal-opener.mjs +5 -19
- package/hub/team/tui-viewer.mjs +2 -2
- package/hub/team/tui.mjs +0 -6
- package/hub/workers/factory.mjs +1 -10
- package/hub/workers/interface.mjs +0 -7
- package/package.json +2 -6
- package/scripts/lib/backup-rotation.mjs +60 -0
- package/scripts/lib/codex-profile-config.mjs +20 -7
- package/scripts/lib/codex-recovery.sh +2 -3
- package/scripts/lib/install-blob-history.json +3196 -0
- package/scripts/lib/install-retire.mjs +203 -0
- package/scripts/lib/legacy-hook-cleanup.mjs +148 -13
- package/scripts/lib/legacy-mcp-cleanup.mjs +405 -104
- package/scripts/lib/machine-profile.mjs +2 -2
- package/scripts/lib/mcp-filter.mjs +1 -42
- package/scripts/lib/mcp-guard-engine.mjs +198 -19
- package/scripts/lib/mcp-manifest.mjs +0 -40
- package/scripts/lib/process-utils.mjs +19 -0
- package/scripts/lib/stable-node.mjs +35 -0
- package/scripts/lib/test-env.mjs +20 -0
- package/cto/brief.mjs +0 -73
- package/cto/collect.mjs +0 -490
- package/cto/current.schema.json +0 -148
- package/cto/events.mjs +0 -208
- package/cto/hygiene.mjs +0 -320
- package/cto/lake-root.mjs +0 -127
- package/cto/status.mjs +0 -124
- package/hub/lib/cto-env.mjs +0 -1
- package/hub/team/notify.mjs +0 -385
- package/hub/team/process-cleanup.mjs +0 -386
- package/hub/team/routing.mjs +0 -249
- package/hub/team/staleState.mjs +0 -403
- package/hub/team/worker-signal.mjs +0 -263
- package/hub/team/worker-signal.types.d.ts +0 -52
- package/hub/workers/codex-mcp.mjs +0 -853
- package/scripts/lib/remote-spawn-transfer.mjs +0 -226
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
readdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
statSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { basename, dirname, join } from "node:path";
|
|
10
|
+
|
|
11
|
+
// 설정이 바뀔 때마다 백업이 쌓여 끝없이 늘었다. 같은 대상의 백업은 최근 몇 개만 남긴다.
|
|
12
|
+
export const BACKUP_KEEP = 5;
|
|
13
|
+
|
|
14
|
+
// 시각 suffix 만 받는다. label 이 더 긴 다른 백업(예: <label>-manual-...)은 남긴다.
|
|
15
|
+
const STAMP_SUFFIX = /^[\dT]+(?:-\d+)*$/i;
|
|
16
|
+
|
|
17
|
+
// 읽는 사이 다른 프로세스가 지웠으면 같은 것으로 보고 아래에서 다시 쓴다. 다른 읽기 오류는 그대로 던진다.
|
|
18
|
+
function sameContent(path, content) {
|
|
19
|
+
try {
|
|
20
|
+
return readFileSync(path, "utf8") === content;
|
|
21
|
+
} catch (error) {
|
|
22
|
+
if (error?.code === "ENOENT") return true;
|
|
23
|
+
throw error;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// `<파일>.<label>-<시각>` 으로 백업하고, 같은 label 의 오래된 백업을 지운다.
|
|
28
|
+
export function writeRotatedBackup(
|
|
29
|
+
path,
|
|
30
|
+
content,
|
|
31
|
+
{ label, suffix, mode, keep = BACKUP_KEEP },
|
|
32
|
+
) {
|
|
33
|
+
const directory = dirname(path);
|
|
34
|
+
const prefix = `${basename(path)}.${label}-`;
|
|
35
|
+
// 같은 시각 이름에 다른 원문이 이미 있으면 번호를 붙여 따로 남긴다.
|
|
36
|
+
let backupPath = join(directory, `${prefix}${suffix}`);
|
|
37
|
+
for (let i = 1; existsSync(backupPath); i++) {
|
|
38
|
+
if (sameContent(backupPath, content)) break;
|
|
39
|
+
backupPath = join(directory, `${prefix}${suffix}-${i}`);
|
|
40
|
+
}
|
|
41
|
+
if (!existsSync(backupPath))
|
|
42
|
+
writeFileSync(backupPath, content, { encoding: "utf8", mode });
|
|
43
|
+
const backups = readdirSync(directory)
|
|
44
|
+
.filter(
|
|
45
|
+
(name) =>
|
|
46
|
+
name.startsWith(prefix) && STAMP_SUFFIX.test(name.slice(prefix.length)),
|
|
47
|
+
)
|
|
48
|
+
.flatMap((name) => {
|
|
49
|
+
// 다른 프로세스가 같은 백업을 먼저 지웠을 수 있다.
|
|
50
|
+
try {
|
|
51
|
+
return [{ name, mtime: statSync(join(directory, name)).mtimeMs }];
|
|
52
|
+
} catch {
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
.sort((a, b) => b.mtime - a.mtime || b.name.localeCompare(a.name));
|
|
57
|
+
for (const { name } of backups.slice(keep))
|
|
58
|
+
rmSync(join(directory, name), { force: true });
|
|
59
|
+
return backupPath;
|
|
60
|
+
}
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
mkdirSync,
|
|
4
4
|
readFileSync,
|
|
5
5
|
renameSync,
|
|
6
|
+
statSync,
|
|
6
7
|
writeFileSync,
|
|
7
8
|
} from "node:fs";
|
|
8
9
|
import { homedir } from "node:os";
|
|
9
10
|
import { dirname, join } from "node:path";
|
|
11
|
+
import { writeRotatedBackup } from "./backup-rotation.mjs";
|
|
10
12
|
|
|
11
13
|
const EFFORT_BY_SUFFIX = {
|
|
12
14
|
ultra: "ultra",
|
|
@@ -80,10 +82,6 @@ function readProfileScalar(raw, key) {
|
|
|
80
82
|
* naming convention when the file is absent. Returns the keys that resolve, in
|
|
81
83
|
* `key="value"` TOML-scalar form ready to be shell-quoted by the caller.
|
|
82
84
|
*
|
|
83
|
-
* NOTE: the Codex MCP transport does the structured-arg equivalent in
|
|
84
|
-
* hub/workers/codex-mcp.mjs (resolveCodexProfileConfig → model + config object);
|
|
85
|
-
* this returns the CLI `-c` string form for the same per-profile file.
|
|
86
|
-
*
|
|
87
85
|
* @param {string} profileName
|
|
88
86
|
* @param {{ codexHome?: string }} [opts]
|
|
89
87
|
* @returns {string[]} e.g. ['model="gpt-6.1-sol"', 'model_reasoning_effort="high"']
|
|
@@ -230,7 +228,8 @@ export function sanitizeCodexProfileConfig(
|
|
|
230
228
|
skippedProfileFiles.push(profileName);
|
|
231
229
|
continue;
|
|
232
230
|
}
|
|
233
|
-
|
|
231
|
+
// 옮겨 온 프로필 본문에 MCP env 키가 있을 수 있다.
|
|
232
|
+
writeFileSync(profilePath, body, { encoding: "utf8", mode: 0o600 });
|
|
234
233
|
migratedProfiles.push(profileName);
|
|
235
234
|
}
|
|
236
235
|
}
|
|
@@ -254,6 +253,15 @@ function stamp(now = () => new Date()) {
|
|
|
254
253
|
.replace("T", "-");
|
|
255
254
|
}
|
|
256
255
|
|
|
256
|
+
// config.toml 에는 MCP env 키가 들어갈 수 있다. 다시 쓸 때 원본 권한을 지키고 없으면 0600 으로 만든다.
|
|
257
|
+
export function privateFileMode(path) {
|
|
258
|
+
try {
|
|
259
|
+
return statSync(path).mode & 0o777;
|
|
260
|
+
} catch {
|
|
261
|
+
return 0o600;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
257
265
|
export function sanitizeCodexProfileConfigFile(
|
|
258
266
|
configPath,
|
|
259
267
|
{
|
|
@@ -268,7 +276,12 @@ export function sanitizeCodexProfileConfigFile(
|
|
|
268
276
|
const source = readFileSync(configPath, "utf8");
|
|
269
277
|
const sanitized = sanitizeCodexProfileConfig(source, { codexHome });
|
|
270
278
|
if (!sanitized.changed) return sanitized;
|
|
271
|
-
|
|
279
|
+
const mode = privateFileMode(configPath);
|
|
280
|
+
writeRotatedBackup(configPath, source, {
|
|
281
|
+
label: backupPrefix,
|
|
282
|
+
suffix: stamp(now),
|
|
283
|
+
mode,
|
|
284
|
+
});
|
|
272
285
|
// Atomic replace. config.toml is shared global state: a codex session may be
|
|
273
286
|
// reading it while another entry point (tfx-route, the codex session hook, or
|
|
274
287
|
// a parallel swarm worker) sanitizes it. Write the sanitized content to a
|
|
@@ -278,7 +291,7 @@ export function sanitizeCodexProfileConfigFile(
|
|
|
278
291
|
// without corruption (the content is identical anyway since the migration is
|
|
279
292
|
// deterministic). The pid keeps temp names unique across separate processes.
|
|
280
293
|
const tmpPath = `${configPath}.tmp-${process.pid}-${stamp(now)}`;
|
|
281
|
-
writeFileSync(tmpPath, sanitized.toml, "utf8");
|
|
294
|
+
writeFileSync(tmpPath, sanitized.toml, { encoding: "utf8", mode });
|
|
282
295
|
renameSync(tmpPath, configPath);
|
|
283
296
|
return sanitized;
|
|
284
297
|
}
|
|
@@ -58,9 +58,8 @@ recover_codex_stdout() {
|
|
|
58
58
|
|
|
59
59
|
# ── MCP transport 채널 사망 판정 ──
|
|
60
60
|
# Codex MCP transport 채널이 실행 중 죽으면 codex exec 가 exit 0 + 빈 stdout 으로
|
|
61
|
-
# 끝나는 변형이
|
|
62
|
-
#
|
|
63
|
-
# 검출한다. 아우터 가드가 exit 0 이어도 결과를 실패로 승격하는 근거로 쓴다.
|
|
61
|
+
# 끝나는 변형이 있다. stderr 의 하드 크래시 서명으로 이 사망을 검출한다.
|
|
62
|
+
# 아우터 가드가 exit 0 이어도 결과를 실패로 승격하는 근거로 쓴다.
|
|
64
63
|
# 인자: [stderr_log] (미지정 시 $STDERR_LOG). 반환: 0=크래시 서명 있음, 1=없음.
|
|
65
64
|
codex_stdout_transport_crashed() {
|
|
66
65
|
local stderr_log="${1:-${STDERR_LOG:-}}"
|