triflux 9.7.13 → 9.8.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.ko.md +2 -0
- package/README.md +2 -0
- package/bin/triflux.mjs +297 -47
- package/hooks/hook-registry.json +4 -4
- package/hub/fullcycle.mjs +96 -0
- package/hub/paths.mjs +30 -28
- package/hub/pipeline/index.mjs +318 -318
- package/hub/schema.sql +146 -146
- package/hub/team/cli/commands/kill.mjs +37 -37
- package/hub/team/cli/commands/stop.mjs +31 -31
- package/hub/team/cli/commands/task.mjs +30 -30
- package/hub/team/cli/services/hub-client.mjs +208 -208
- package/hub/team/cli/services/native-control.mjs +118 -118
- package/hub/team/cli/services/runtime-mode.mjs +62 -62
- package/hub/team/cli/services/state-store.mjs +48 -48
- package/hub/team/dashboard.mjs +274 -274
- package/hub/team/native.mjs +649 -649
- package/hub/team/psmux.mjs +68 -13
- package/hub/tools.mjs +554 -554
- package/hub/workers/claude-worker.mjs +423 -423
- package/hub/workers/codex-mcp.mjs +410 -410
- package/hub/workers/gemini-worker.mjs +429 -429
- package/hub/workers/interface.mjs +40 -40
- package/package.json +1 -1
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +1 -1
- package/scripts/cache-warmup.mjs +1 -0
- package/scripts/claude-logged.ps1 +54 -0
- package/scripts/demo-tui.mjs +59 -0
- package/scripts/headless-guard.mjs +4 -7
- package/scripts/hub-ensure.mjs +120 -120
- package/scripts/lib/psmux-info.mjs +119 -0
- package/scripts/lib/remote-spawn-transfer.mjs +1 -1
- package/scripts/setup.mjs +150 -6
- package/scripts/tfx-route-post.mjs +90 -13
- package/scripts/token-snapshot.mjs +575 -575
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/tfx-codex-swarm/SKILL.md +40 -5
- package/skills/tfx-codex-swarm/mcp-daemon/register-autostart.ps1 +32 -0
- package/skills/tfx-doctor/SKILL.md +3 -0
- package/skills/tfx-fullcycle/SKILL.md +79 -4
- package/skills/tfx-hub/SKILL.md +3 -1
- package/skills/tfx-psmux-rules/SKILL.md +53 -31
- package/skills/tfx-remote-spawn/references/hosts.json +16 -16
- package/skills/tfx-setup/SKILL.md +9 -0
- package/tui/doctor.mjs +1 -0
|
@@ -1,62 +1,62 @@
|
|
|
1
|
-
import {
|
|
2
|
-
detectMultiplexer,
|
|
3
|
-
hasWindowsTerminal,
|
|
4
|
-
hasWindowsTerminalSession,
|
|
5
|
-
sessionExists,
|
|
6
|
-
} from "../../session.mjs";
|
|
7
|
-
|
|
8
|
-
export function normalizeTeammateMode(mode = "auto") {
|
|
9
|
-
const raw = String(mode).toLowerCase();
|
|
10
|
-
if (raw === "inline" || raw === "native") return "in-process";
|
|
11
|
-
if (raw === "headless" || raw === "hl") return "headless";
|
|
12
|
-
if (raw === "psmux") return "headless";
|
|
13
|
-
if (raw === "in-process" || raw === "tmux" || raw === "wt") return raw;
|
|
14
|
-
if (raw === "windows-terminal" || raw === "windows_terminal") return "wt";
|
|
15
|
-
if (raw === "auto") {
|
|
16
|
-
if (process.env.TMUX) return "tmux";
|
|
17
|
-
return detectMultiplexer() === "psmux" ? "headless" : "in-process";
|
|
18
|
-
}
|
|
19
|
-
return "in-process";
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function normalizeLayout(layout = "2x2") {
|
|
23
|
-
const raw = String(layout).toLowerCase();
|
|
24
|
-
if (raw === "2x2" || raw === "grid") return "2x2";
|
|
25
|
-
if (raw === "1xn" || raw === "1x3" || raw === "vertical" || raw === "columns") return "1xN";
|
|
26
|
-
if (raw === "nx1" || raw === "horizontal" || raw === "rows") return "Nx1";
|
|
27
|
-
return "2x2";
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function isNativeMode(state) {
|
|
31
|
-
return state?.teammateMode === "in-process" && !!state?.native?.controlUrl;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function isWtMode(state) {
|
|
35
|
-
return state?.teammateMode === "wt";
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function isTeamAlive(state) {
|
|
39
|
-
if (!state) return false;
|
|
40
|
-
if (isNativeMode(state)) {
|
|
41
|
-
try {
|
|
42
|
-
process.kill(state.native.supervisorPid, 0);
|
|
43
|
-
return true;
|
|
44
|
-
} catch {
|
|
45
|
-
return false;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
if (isWtMode(state)) {
|
|
49
|
-
if (!hasWindowsTerminal()) return false;
|
|
50
|
-
if (hasWindowsTerminalSession()) return true;
|
|
51
|
-
return Array.isArray(state.members) && state.members.length > 0;
|
|
52
|
-
}
|
|
53
|
-
return sessionExists(state.sessionName);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export function ensureTmuxOrExit() {
|
|
57
|
-
const mux = detectMultiplexer();
|
|
58
|
-
if (mux) return mux;
|
|
59
|
-
const error = new Error("tmux 미발견");
|
|
60
|
-
error.code = "TMUX_REQUIRED";
|
|
61
|
-
throw error;
|
|
62
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
detectMultiplexer,
|
|
3
|
+
hasWindowsTerminal,
|
|
4
|
+
hasWindowsTerminalSession,
|
|
5
|
+
sessionExists,
|
|
6
|
+
} from "../../session.mjs";
|
|
7
|
+
|
|
8
|
+
export function normalizeTeammateMode(mode = "auto") {
|
|
9
|
+
const raw = String(mode).toLowerCase();
|
|
10
|
+
if (raw === "inline" || raw === "native") return "in-process";
|
|
11
|
+
if (raw === "headless" || raw === "hl") return "headless";
|
|
12
|
+
if (raw === "psmux") return "headless";
|
|
13
|
+
if (raw === "in-process" || raw === "tmux" || raw === "wt") return raw;
|
|
14
|
+
if (raw === "windows-terminal" || raw === "windows_terminal") return "wt";
|
|
15
|
+
if (raw === "auto") {
|
|
16
|
+
if (process.env.TMUX) return "tmux";
|
|
17
|
+
return detectMultiplexer() === "psmux" ? "headless" : "in-process";
|
|
18
|
+
}
|
|
19
|
+
return "in-process";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function normalizeLayout(layout = "2x2") {
|
|
23
|
+
const raw = String(layout).toLowerCase();
|
|
24
|
+
if (raw === "2x2" || raw === "grid") return "2x2";
|
|
25
|
+
if (raw === "1xn" || raw === "1x3" || raw === "vertical" || raw === "columns") return "1xN";
|
|
26
|
+
if (raw === "nx1" || raw === "horizontal" || raw === "rows") return "Nx1";
|
|
27
|
+
return "2x2";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isNativeMode(state) {
|
|
31
|
+
return state?.teammateMode === "in-process" && !!state?.native?.controlUrl;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function isWtMode(state) {
|
|
35
|
+
return state?.teammateMode === "wt";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function isTeamAlive(state) {
|
|
39
|
+
if (!state) return false;
|
|
40
|
+
if (isNativeMode(state)) {
|
|
41
|
+
try {
|
|
42
|
+
process.kill(state.native.supervisorPid, 0);
|
|
43
|
+
return true;
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (isWtMode(state)) {
|
|
49
|
+
if (!hasWindowsTerminal()) return false;
|
|
50
|
+
if (hasWindowsTerminalSession()) return true;
|
|
51
|
+
return Array.isArray(state.members) && state.members.length > 0;
|
|
52
|
+
}
|
|
53
|
+
return sessionExists(state.sessionName);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function ensureTmuxOrExit() {
|
|
57
|
+
const mux = detectMultiplexer();
|
|
58
|
+
if (mux) return mux;
|
|
59
|
+
const error = new Error("tmux 미발견");
|
|
60
|
+
error.code = "TMUX_REQUIRED";
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { mkdirSync } from "node:fs";
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
import { homedir } from "node:os";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
export const PKG_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
8
|
-
export const HUB_PID_DIR = join(homedir(), ".claude", "cache", "tfx-hub");
|
|
9
|
-
export const TEAM_PROFILE = (() => {
|
|
10
|
-
const raw = String(process.env.TFX_TEAM_PROFILE || "team").trim().toLowerCase();
|
|
11
|
-
return raw === "codex-team" ? "codex-team" : "team";
|
|
12
|
-
})();
|
|
13
|
-
|
|
14
|
-
export const SESSION_ID = process.env.CLAUDE_SESSION_ID || `s${Date.now()}`;
|
|
15
|
-
|
|
16
|
-
function getStatePath(sessionId) {
|
|
17
|
-
if (sessionId) return join(HUB_PID_DIR, `team-state-${sessionId}.json`);
|
|
18
|
-
return join(HUB_PID_DIR, TEAM_PROFILE === "codex-team" ? "team-state-codex-team.json" : "team-state.json");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function loadTeamState(sessionId) {
|
|
22
|
-
const resolvedId = sessionId || SESSION_ID;
|
|
23
|
-
const sessionPath = getStatePath(resolvedId);
|
|
24
|
-
try {
|
|
25
|
-
if (existsSync(sessionPath)) return JSON.parse(readFileSync(sessionPath, "utf8"));
|
|
26
|
-
} catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
// 세션별 파일 없으면 기존 team-state.json fallback
|
|
30
|
-
const legacyPath = getStatePath(null);
|
|
31
|
-
try {
|
|
32
|
-
if (existsSync(legacyPath)) return JSON.parse(readFileSync(legacyPath, "utf8"));
|
|
33
|
-
} catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export function saveTeamState(state, sessionId) {
|
|
40
|
-
const path = getStatePath(sessionId || state.sessionId || SESSION_ID);
|
|
41
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
-
writeFileSync(path, JSON.stringify({ ...state, profile: TEAM_PROFILE }, null, 2) + "\n");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export function clearTeamState(sessionId) {
|
|
46
|
-
const path = getStatePath(sessionId || SESSION_ID);
|
|
47
|
-
if (existsSync(path)) unlinkSync(path);
|
|
48
|
-
}
|
|
1
|
+
import { existsSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { mkdirSync } from "node:fs";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
export const PKG_ROOT = fileURLToPath(new URL("../../../../", import.meta.url));
|
|
8
|
+
export const HUB_PID_DIR = join(homedir(), ".claude", "cache", "tfx-hub");
|
|
9
|
+
export const TEAM_PROFILE = (() => {
|
|
10
|
+
const raw = String(process.env.TFX_TEAM_PROFILE || "team").trim().toLowerCase();
|
|
11
|
+
return raw === "codex-team" ? "codex-team" : "team";
|
|
12
|
+
})();
|
|
13
|
+
|
|
14
|
+
export const SESSION_ID = process.env.CLAUDE_SESSION_ID || `s${Date.now()}`;
|
|
15
|
+
|
|
16
|
+
function getStatePath(sessionId) {
|
|
17
|
+
if (sessionId) return join(HUB_PID_DIR, `team-state-${sessionId}.json`);
|
|
18
|
+
return join(HUB_PID_DIR, TEAM_PROFILE === "codex-team" ? "team-state-codex-team.json" : "team-state.json");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function loadTeamState(sessionId) {
|
|
22
|
+
const resolvedId = sessionId || SESSION_ID;
|
|
23
|
+
const sessionPath = getStatePath(resolvedId);
|
|
24
|
+
try {
|
|
25
|
+
if (existsSync(sessionPath)) return JSON.parse(readFileSync(sessionPath, "utf8"));
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
// 세션별 파일 없으면 기존 team-state.json fallback
|
|
30
|
+
const legacyPath = getStatePath(null);
|
|
31
|
+
try {
|
|
32
|
+
if (existsSync(legacyPath)) return JSON.parse(readFileSync(legacyPath, "utf8"));
|
|
33
|
+
} catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function saveTeamState(state, sessionId) {
|
|
40
|
+
const path = getStatePath(sessionId || state.sessionId || SESSION_ID);
|
|
41
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
42
|
+
writeFileSync(path, JSON.stringify({ ...state, profile: TEAM_PROFILE }, null, 2) + "\n");
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function clearTeamState(sessionId) {
|
|
46
|
+
const path = getStatePath(sessionId || SESSION_ID);
|
|
47
|
+
if (existsSync(path)) unlinkSync(path);
|
|
48
|
+
}
|