@triflux/remote 10.0.1 → 10.25.1
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/pipe.mjs +239 -121
- package/hub/public/dashboard.html +68 -7
- package/hub/server.mjs +2181 -701
- package/hub/store-adapter.mjs +101 -37
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +26 -7
- package/hub/team/ansi.mjs +245 -146
- package/hub/team/backend.mjs +84 -17
- package/hub/team/build-worker-prompt.mjs +42 -0
- package/hub/team/check-mcp-hub.mjs +49 -0
- package/hub/team/claude-daemon-control.mjs +219 -0
- package/hub/team/claude-native-bridge.mjs +784 -0
- package/hub/team/claude-session-projection.mjs +65 -0
- package/hub/team/cli/commands/attach.mjs +57 -37
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +95 -74
- package/hub/team/cli/commands/focus.mjs +80 -53
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +31 -24
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -33
- package/hub/team/cli/commands/start/parse-args.mjs +89 -12
- package/hub/team/cli/commands/start/start-headless.mjs +127 -37
- package/hub/team/cli/commands/start/start-in-process.mjs +50 -40
- package/hub/team/cli/commands/start/start-mux.mjs +122 -73
- package/hub/team/cli/commands/start/start-wt.mjs +86 -69
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +13 -13
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +52 -30
- package/hub/team/cli/services/attach-fallback.mjs +74 -54
- package/hub/team/cli/services/hub-client.mjs +45 -17
- package/hub/team/cli/services/member-selector.mjs +37 -30
- package/hub/team/cli/services/native-control.mjs +144 -116
- package/hub/team/cli/services/runtime-mode.mjs +55 -1
- package/hub/team/cli/services/state-store.mjs +66 -9
- package/hub/team/cli/services/task-model.mjs +40 -30
- package/hub/team/codex-review.mjs +521 -0
- package/hub/team/conductor-mesh-bridge.mjs +25 -24
- package/hub/team/conductor-registry.mjs +121 -0
- package/hub/team/conductor.mjs +1379 -671
- package/hub/team/consensus-meta.mjs +176 -0
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +21 -119
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/execution-mode.mjs +241 -0
- package/hub/team/extract-completion-payload.mjs +110 -0
- package/hub/team/git-preflight.mjs +286 -0
- package/hub/team/handoff.mjs +49 -18
- package/hub/team/headless-bridge-session.mjs +17 -0
- package/hub/team/headless.mjs +2065 -1156
- package/hub/team/health-probe.mjs +232 -69
- package/hub/team/launcher-template.mjs +23 -13
- package/hub/team/lead-control.mjs +91 -4
- package/hub/team/mcp-selector.mjs +145 -0
- package/hub/team/native-supervisor.mjs +90 -26
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +147 -78
- package/hub/team/orchestrator.mjs +173 -161
- package/hub/team/pane.mjs +43 -15
- package/hub/team/process-cleanup.mjs +69 -25
- package/hub/team/psmux.mjs +1067 -264
- package/hub/team/recovery-store.mjs +149 -0
- package/hub/team/remote-probe.mjs +69 -53
- package/hub/team/remote-session.mjs +131 -59
- package/hub/team/remote-watcher.mjs +62 -34
- package/hub/team/retry-state-machine.mjs +267 -0
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +200 -0
- package/hub/team/schemas/worker-completion.json +39 -0
- package/hub/team/sentinel-capture.mjs +127 -0
- package/hub/team/session-sync.mjs +153 -6
- package/hub/team/session.mjs +605 -611
- package/hub/team/shared.mjs +13 -13
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-cli.mjs +390 -0
- package/hub/team/swarm-hypervisor.mjs +1526 -198
- package/hub/team/swarm-intent.mjs +216 -0
- package/hub/team/swarm-locks.mjs +140 -25
- package/hub/team/swarm-planner.mjs +121 -21
- package/hub/team/swarm-preflight.mjs +317 -0
- package/hub/team/swarm-reconciler.mjs +128 -23
- package/hub/team/synapse-cli.mjs +458 -0
- package/hub/team/synapse-http.mjs +60 -0
- package/hub/team/synapse-registry.mjs +329 -0
- package/hub/team/terminal-opener.mjs +216 -0
- package/hub/team/tui-core.mjs +288 -0
- package/hub/team/tui-lite.mjs +234 -146
- package/hub/team/tui-remote-adapter.mjs +21 -37
- package/hub/team/tui-synapse.mjs +223 -0
- package/hub/team/tui-viewer.mjs +104 -45
- package/hub/team/tui-widgets.mjs +262 -0
- package/hub/team/tui.mjs +936 -449
- package/hub/team/worker-completion-validator.mjs +84 -0
- package/hub/team/worker-sandbox.mjs +93 -0
- package/hub/team/worker-signal.mjs +263 -0
- package/hub/team/worker-signal.types.d.ts +52 -0
- package/hub/team/worktree-lifecycle.mjs +518 -64
- package/hub/team/wt-manager.mjs +297 -26
- package/hub/tools.mjs +474 -252
- package/hub/tray.mjs +411 -368
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-app-server-worker.mjs +1015 -0
- package/hub/workers/codex-mcp.mjs +724 -507
- package/hub/workers/delegator-mcp.mjs +663 -337
- package/hub/workers/factory.mjs +100 -21
- package/hub/workers/gemini-worker.mjs +179 -86
- package/hub/workers/interface.mjs +23 -2
- package/hub/workers/lib/jsonrpc-stdio.mjs +463 -0
- package/hub/workers/worker-utils.mjs +22 -16
- package/package.json +4 -3
- package/scripts/lib/agent-json.mjs +27 -0
- package/scripts/lib/async.mjs +174 -0
- package/scripts/lib/claudemd-scanner.mjs +223 -0
- package/scripts/lib/cli-agy.mjs +62 -0
- package/scripts/lib/cli-claude.mjs +78 -0
- package/scripts/lib/cli-codex.mjs +199 -0
- package/scripts/lib/cli-gemini.mjs +67 -0
- package/scripts/lib/codex-recovery.sh +50 -0
- package/scripts/lib/context.mjs +67 -0
- package/scripts/lib/cross-review-utils.mjs +59 -0
- package/scripts/lib/doctor-env-checks.mjs +121 -0
- package/scripts/lib/dynamic-route-cli.mjs +107 -0
- package/scripts/lib/env-probe.mjs +269 -0
- package/scripts/lib/env.mjs +14 -0
- package/scripts/lib/gemini-profiles.mjs +119 -0
- package/scripts/lib/handoff.mjs +187 -0
- package/scripts/lib/hook-utils.mjs +16 -0
- package/scripts/lib/hub.mjs +48 -0
- package/scripts/lib/keyword-rules.mjs +195 -0
- package/scripts/lib/logger.mjs +105 -0
- package/scripts/lib/mcp-filter.mjs +883 -0
- package/scripts/lib/mcp-gateway-servers.mjs +70 -0
- package/scripts/lib/mcp-guard-engine.mjs +2120 -0
- package/scripts/lib/mcp-health.mjs +773 -0
- package/scripts/lib/mcp-manifest.mjs +89 -0
- package/scripts/lib/mcp-server-catalog.mjs +355 -0
- package/scripts/lib/pid.mjs +63 -0
- package/scripts/lib/process-utils.mjs +26 -0
- package/scripts/lib/psmux-info.mjs +179 -0
- package/scripts/lib/quota.mjs +47 -0
- package/scripts/lib/remote-spawn-transfer.mjs +226 -0
- package/scripts/lib/skill-state.mjs +220 -0
- package/scripts/lib/skill-template.mjs +375 -0
- package/scripts/lib/team.mjs +75 -0
- package/scripts/lib/timeout.mjs +66 -0
- package/scripts/lib/tmp.mjs +26 -0
- package/scripts/lib/toml.mjs +113 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
export function buildClaudeSessionProjection({
|
|
5
|
+
pid,
|
|
6
|
+
procStart,
|
|
7
|
+
sessionId,
|
|
8
|
+
short,
|
|
9
|
+
cwd,
|
|
10
|
+
name,
|
|
11
|
+
agent,
|
|
12
|
+
startedAt = Date.now(),
|
|
13
|
+
updatedAt = Date.now(),
|
|
14
|
+
status = "busy",
|
|
15
|
+
version = "triflux-native-bridge",
|
|
16
|
+
} = {}) {
|
|
17
|
+
if (!Number.isInteger(pid) || pid <= 0) throw new Error("pid is required");
|
|
18
|
+
if (!procStart) throw new Error("procStart is required");
|
|
19
|
+
if (!sessionId) throw new Error("sessionId is required");
|
|
20
|
+
if (!short) throw new Error("short is required");
|
|
21
|
+
if (!cwd) throw new Error("cwd is required");
|
|
22
|
+
if (!name) throw new Error("name is required");
|
|
23
|
+
if (!agent) throw new Error("agent is required");
|
|
24
|
+
return {
|
|
25
|
+
pid,
|
|
26
|
+
sessionId,
|
|
27
|
+
cwd,
|
|
28
|
+
startedAt,
|
|
29
|
+
procStart,
|
|
30
|
+
version,
|
|
31
|
+
peerProtocol: 1,
|
|
32
|
+
kind: "bg",
|
|
33
|
+
entrypoint: "cli",
|
|
34
|
+
name,
|
|
35
|
+
agent,
|
|
36
|
+
jobId: short,
|
|
37
|
+
status,
|
|
38
|
+
updatedAt,
|
|
39
|
+
bridgeSessionId: `session_${short}`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function writeClaudeSessionProjection(sessionsDir, projection) {
|
|
44
|
+
await fs.mkdir(sessionsDir, { recursive: true });
|
|
45
|
+
const sessionPath = path.join(sessionsDir, `${projection.pid}.json`);
|
|
46
|
+
const tmpPath = `${sessionPath}.${process.pid}.tmp`;
|
|
47
|
+
await fs.writeFile(tmpPath, `${JSON.stringify(projection)}\n`, "utf8");
|
|
48
|
+
await fs.rename(tmpPath, sessionPath);
|
|
49
|
+
return sessionPath;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export async function updateClaudeSessionProjection(sessionPath, patch) {
|
|
53
|
+
const parsed = JSON.parse(await fs.readFile(sessionPath, "utf8"));
|
|
54
|
+
const next = {
|
|
55
|
+
...parsed,
|
|
56
|
+
...patch,
|
|
57
|
+
updatedAt: patch.updatedAt || Date.now(),
|
|
58
|
+
};
|
|
59
|
+
await fs.writeFile(sessionPath, `${JSON.stringify(next)}\n`, "utf8");
|
|
60
|
+
return next;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function removeClaudeSessionProjection(sessionPath) {
|
|
64
|
+
await fs.rm(sessionPath, { force: true });
|
|
65
|
+
}
|
|
@@ -1,37 +1,57 @@
|
|
|
1
|
-
import { attachSession } from "../../session.mjs";
|
|
2
|
-
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
if (
|
|
19
|
-
console.log(`\n ${DIM}
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
1
|
+
import { attachSession } from "../../session.mjs";
|
|
2
|
+
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
+
import { fail, ok, warn } from "../render.mjs";
|
|
4
|
+
import {
|
|
5
|
+
buildManualAttachCommand,
|
|
6
|
+
launchAttachInWindowsTerminal,
|
|
7
|
+
wantsWtAttachFallback,
|
|
8
|
+
} from "../services/attach-fallback.mjs";
|
|
9
|
+
import {
|
|
10
|
+
isNativeMode,
|
|
11
|
+
isTeamAlive,
|
|
12
|
+
isWtMode,
|
|
13
|
+
} from "../services/runtime-mode.mjs";
|
|
14
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
15
|
+
|
|
16
|
+
export async function teamAttach(args = []) {
|
|
17
|
+
const state = loadTeamState();
|
|
18
|
+
if (!state || !isTeamAlive(state)) {
|
|
19
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
if (isNativeMode(state)) {
|
|
23
|
+
console.log(
|
|
24
|
+
`\n ${DIM}in-process 모드는 별도 attach가 없습니다.${RESET}\n ${DIM}상태 확인: tfx multi status${RESET}\n`,
|
|
25
|
+
);
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (isWtMode(state)) {
|
|
29
|
+
console.log(
|
|
30
|
+
`\n ${DIM}wt 모드는 attach 개념이 없습니다 (Windows Terminal pane가 독립 실행됨).${RESET}\n ${DIM}재실행/정리는: tfx multi stop${RESET}\n`,
|
|
31
|
+
);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
attachSession(state.sessionName);
|
|
37
|
+
} catch (error) {
|
|
38
|
+
const allowWt = wantsWtAttachFallback(args);
|
|
39
|
+
if (allowWt && (await launchAttachInWindowsTerminal(state.sessionName))) {
|
|
40
|
+
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
41
|
+
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
42
|
+
console.log(
|
|
43
|
+
` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
|
|
44
|
+
);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
fail(`attach 실패: ${error.message}`);
|
|
48
|
+
warn(
|
|
49
|
+
allowWt
|
|
50
|
+
? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)"
|
|
51
|
+
: "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.",
|
|
52
|
+
);
|
|
53
|
+
console.log(
|
|
54
|
+
` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import { injectPrompt, sendKeys } from "../../pane.mjs";
|
|
2
2
|
import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
|
|
3
|
+
import { ok, warn } from "../render.mjs";
|
|
3
4
|
import { publishLeadControl } from "../services/hub-client.mjs";
|
|
4
5
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
5
6
|
import { nativeRequest } from "../services/native-control.mjs";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
isNativeMode,
|
|
9
|
+
isTeamAlive,
|
|
10
|
+
isWtMode,
|
|
11
|
+
} from "../services/runtime-mode.mjs";
|
|
7
12
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
8
|
-
import { ok, warn } from "../render.mjs";
|
|
9
13
|
|
|
10
14
|
export async function teamControl(args = []) {
|
|
11
15
|
const state = loadTeamState();
|
|
@@ -17,26 +21,43 @@ export async function teamControl(args = []) {
|
|
|
17
21
|
const member = resolveMember(state, args[0]);
|
|
18
22
|
const command = String(args[1] || "").toLowerCase();
|
|
19
23
|
const reason = args.slice(2).join(" ");
|
|
20
|
-
if (
|
|
21
|
-
|
|
24
|
+
if (
|
|
25
|
+
!member ||
|
|
26
|
+
!new Set(["interrupt", "stop", "pause", "resume"]).has(command)
|
|
27
|
+
) {
|
|
28
|
+
console.log(
|
|
29
|
+
`\n 사용법: ${WHITE}tfx multi control <lead|이름|번호> <interrupt|stop|pause|resume> [사유]${RESET}\n`,
|
|
30
|
+
);
|
|
22
31
|
return;
|
|
23
32
|
}
|
|
24
33
|
if (isWtMode(state)) {
|
|
25
|
-
console.log(
|
|
34
|
+
console.log(
|
|
35
|
+
`\n ${YELLOW}⚠${RESET} wt 모드는 Hub direct/control 주입 경로가 비활성입니다.\n ${DIM}수동 제어: 해당 pane에서 직접 명령/인터럽트를 수행하세요.${RESET}\n`,
|
|
36
|
+
);
|
|
26
37
|
return;
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
let directOk = false;
|
|
30
41
|
if (isNativeMode(state)) {
|
|
31
|
-
directOk = !!(
|
|
42
|
+
directOk = !!(
|
|
43
|
+
await nativeRequest(state, "/control", {
|
|
44
|
+
member: member.name,
|
|
45
|
+
command,
|
|
46
|
+
reason,
|
|
47
|
+
})
|
|
48
|
+
)?.ok;
|
|
32
49
|
} else {
|
|
33
|
-
injectPrompt(
|
|
50
|
+
injectPrompt(
|
|
51
|
+
member.pane,
|
|
52
|
+
`[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
|
|
53
|
+
);
|
|
34
54
|
if (command === "interrupt") sendKeys(member.pane, "C-c");
|
|
35
55
|
directOk = true;
|
|
36
56
|
}
|
|
37
57
|
|
|
38
58
|
const published = await publishLeadControl(state, member, command, reason);
|
|
39
|
-
if (directOk && published)
|
|
59
|
+
if (directOk && published)
|
|
60
|
+
ok(`${member.name} 제어 전송 (${command}, direct + hub)`);
|
|
40
61
|
else if (directOk) ok(`${member.name} 제어 전송 (${command}, direct only)`);
|
|
41
62
|
else warn(`${member.name} 제어 전송 실패 (${command})`);
|
|
42
63
|
console.log("");
|
|
@@ -1,74 +1,95 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "../../
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
console.log(`
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
console.log(
|
|
37
|
-
|
|
38
|
-
console.log(`
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (
|
|
57
|
-
console.log(`\n ${BOLD}
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
1
|
+
import {
|
|
2
|
+
capturePaneOutput,
|
|
3
|
+
detectMultiplexer,
|
|
4
|
+
getSessionAttachedCount,
|
|
5
|
+
hasWindowsTerminal,
|
|
6
|
+
hasWindowsTerminalSession,
|
|
7
|
+
listSessions,
|
|
8
|
+
} from "../../session.mjs";
|
|
9
|
+
import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
|
|
10
|
+
import { formatCompletionSuffix } from "../render.mjs";
|
|
11
|
+
import { getHubInfo, nativeGetStatus } from "../services/hub-client.mjs";
|
|
12
|
+
import {
|
|
13
|
+
isNativeMode,
|
|
14
|
+
isTeamAlive,
|
|
15
|
+
isWtMode,
|
|
16
|
+
} from "../services/runtime-mode.mjs";
|
|
17
|
+
import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
|
|
18
|
+
|
|
19
|
+
export async function teamDebug(args = []) {
|
|
20
|
+
const state = loadTeamState();
|
|
21
|
+
const flagIndex = args.findIndex((arg) => arg === "--lines" || arg === "-n");
|
|
22
|
+
const lines =
|
|
23
|
+
flagIndex === -1
|
|
24
|
+
? 20
|
|
25
|
+
: Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
|
|
26
|
+
const hub = await getHubInfo();
|
|
27
|
+
|
|
28
|
+
console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
|
|
29
|
+
console.log(` platform: ${process.platform}`);
|
|
30
|
+
console.log(` node: ${process.version}`);
|
|
31
|
+
console.log(
|
|
32
|
+
` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`,
|
|
33
|
+
);
|
|
34
|
+
console.log(` mux: ${detectMultiplexer() || "none"}`);
|
|
35
|
+
console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
|
|
36
|
+
console.log(` hub-url: ${hub?.url || "-"}`);
|
|
37
|
+
const sessions = listSessions();
|
|
38
|
+
console.log(` sessions: ${sessions.length ? sessions.join(", ") : "-"}`);
|
|
39
|
+
|
|
40
|
+
if (!state) {
|
|
41
|
+
console.log(`\n ${DIM}team-state 없음 (활성 세션 없음)${RESET}\n`);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`\n ${BOLD}state${RESET}`);
|
|
46
|
+
console.log(` session: ${state.sessionName}`);
|
|
47
|
+
console.log(` profile: ${state.profile || TEAM_PROFILE}`);
|
|
48
|
+
console.log(` mode: ${state.teammateMode || "tmux"}`);
|
|
49
|
+
console.log(` lead: ${state.lead}`);
|
|
50
|
+
console.log(` agents: ${(state.agents || []).join(", ")}`);
|
|
51
|
+
console.log(` alive: ${isTeamAlive(state) ? "yes" : "no"}`);
|
|
52
|
+
console.log(
|
|
53
|
+
` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`,
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
if (isWtMode(state)) {
|
|
57
|
+
console.log(`\n ${BOLD}wt-session${RESET}`);
|
|
58
|
+
console.log(` window: ${state?.wt?.windowId ?? 0}`);
|
|
59
|
+
console.log(` layout: ${state?.wt?.layout || state?.layout || "-"}`);
|
|
60
|
+
console.log(
|
|
61
|
+
` panes: ${state?.wt?.paneCount ?? (state.members || []).length}`,
|
|
62
|
+
);
|
|
63
|
+
console.log(` wt.exe: ${hasWindowsTerminal() ? "yes" : "no"}`);
|
|
64
|
+
console.log(` WT_SESSION:${hasWindowsTerminalSession() ? "yes" : "no"}`);
|
|
65
|
+
console.log("");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isNativeMode(state)) {
|
|
70
|
+
console.log(`\n ${BOLD}native-members${RESET}`);
|
|
71
|
+
const members = (await nativeGetStatus(state))?.data?.members || [];
|
|
72
|
+
if (!members.length) console.log(` ${DIM}(no data)${RESET}`);
|
|
73
|
+
for (const member of members)
|
|
74
|
+
console.log(
|
|
75
|
+
` - ${member.name}: ${member.status}${formatCompletionSuffix(member)}${member.lastPreview ? ` ${DIM}${member.lastPreview}${RESET}` : ""}`,
|
|
76
|
+
);
|
|
77
|
+
console.log("");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
console.log(
|
|
82
|
+
`\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`,
|
|
83
|
+
);
|
|
84
|
+
if (!(state.members || []).length)
|
|
85
|
+
console.log(` ${DIM}(members 없음)${RESET}`);
|
|
86
|
+
for (const member of state.members || []) {
|
|
87
|
+
console.log(`\n [${member.name}] ${member.pane}`);
|
|
88
|
+
for (const line of (capturePaneOutput(member.pane, lines) || "(empty)")
|
|
89
|
+
.split("\n")
|
|
90
|
+
.slice(-lines)) {
|
|
91
|
+
console.log(` ${line}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
console.log("");
|
|
95
|
+
}
|
|
@@ -1,53 +1,80 @@
|
|
|
1
|
-
import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
|
|
2
|
-
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
1
|
+
import { attachSession, focusPane, focusWtPane } from "../../session.mjs";
|
|
2
|
+
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
+
import { fail, ok, warn } from "../render.mjs";
|
|
4
|
+
import {
|
|
5
|
+
buildManualAttachCommand,
|
|
6
|
+
launchAttachInWindowsTerminal,
|
|
7
|
+
wantsWtAttachFallback,
|
|
8
|
+
} from "../services/attach-fallback.mjs";
|
|
9
|
+
import { resolveMember } from "../services/member-selector.mjs";
|
|
10
|
+
import {
|
|
11
|
+
isNativeMode,
|
|
12
|
+
isTeamAlive,
|
|
13
|
+
isWtMode,
|
|
14
|
+
} from "../services/runtime-mode.mjs";
|
|
15
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
16
|
+
|
|
17
|
+
export async function teamFocus(args = []) {
|
|
18
|
+
const state = loadTeamState();
|
|
19
|
+
if (!state || !isTeamAlive(state)) {
|
|
20
|
+
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (isNativeMode(state)) {
|
|
24
|
+
console.log(
|
|
25
|
+
`\n ${DIM}in-process 모드는 focus/attach 개념이 없습니다.${RESET}\n ${DIM}직접 지시: tfx multi send <대상> "메시지"${RESET}\n`,
|
|
26
|
+
);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const member = resolveMember(state, args[0]);
|
|
31
|
+
if (!member) {
|
|
32
|
+
console.log(
|
|
33
|
+
`\n 사용법: ${WHITE}tfx multi focus <lead|이름|번호>${RESET}\n`,
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (isWtMode(state)) {
|
|
39
|
+
const paneIndex = Number(/^wt:(\d+)$/.exec(member.pane || "")?.[1]);
|
|
40
|
+
if (!Number.isFinite(paneIndex)) {
|
|
41
|
+
console.log(
|
|
42
|
+
`\n ${DIM}wt pane 인덱스 파싱 실패: ${member.pane}${RESET}\n`,
|
|
43
|
+
);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (
|
|
47
|
+
focusWtPane(paneIndex, {
|
|
48
|
+
layout: state?.wt?.layout || state?.layout || "1xN",
|
|
49
|
+
})
|
|
50
|
+
)
|
|
51
|
+
ok(`${member.name} pane 포커스 이동 (wt)`);
|
|
52
|
+
else warn("wt pane 포커스 이동 실패 (WT_SESSION/wt.exe 상태 확인 필요)");
|
|
53
|
+
console.log("");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
focusPane(member.pane, { zoom: false });
|
|
58
|
+
try {
|
|
59
|
+
attachSession(state.sessionName);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
const allowWt = wantsWtAttachFallback(args);
|
|
62
|
+
if (allowWt && (await launchAttachInWindowsTerminal(state.sessionName))) {
|
|
63
|
+
warn(`현재 터미널에서 attach 실패: ${error.message}`);
|
|
64
|
+
ok("Windows Terminal split-pane로 attach 재시도 창을 열었습니다.");
|
|
65
|
+
console.log(
|
|
66
|
+
` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
|
|
67
|
+
);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
fail(`attach 실패: ${error.message}`);
|
|
71
|
+
warn(
|
|
72
|
+
allowWt
|
|
73
|
+
? "WT 분할창 attach 자동 검증 실패 (session_attached 증가 없음)"
|
|
74
|
+
: "자동 WT 분할은 기본 비활성입니다. 필요 시 --wt 옵션으로 실행하세요.",
|
|
75
|
+
);
|
|
76
|
+
console.log(
|
|
77
|
+
` ${DIM}수동 attach 명령: ${buildManualAttachCommand(state.sessionName)}${RESET}\n`,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { sendKeys } from "../../pane.mjs";
|
|
2
2
|
import { DIM, RESET, WHITE, YELLOW } from "../../shared.mjs";
|
|
3
|
+
import { ok, warn } from "../render.mjs";
|
|
3
4
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
4
5
|
import { nativeRequest } from "../services/native-control.mjs";
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
isNativeMode,
|
|
8
|
+
isTeamAlive,
|
|
9
|
+
isWtMode,
|
|
10
|
+
} from "../services/runtime-mode.mjs";
|
|
6
11
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
7
|
-
import { ok, warn } from "../render.mjs";
|
|
8
12
|
|
|
9
13
|
export async function teamInterrupt(args = []) {
|
|
10
14
|
const state = loadTeamState();
|
|
@@ -15,17 +19,25 @@ export async function teamInterrupt(args = []) {
|
|
|
15
19
|
|
|
16
20
|
const member = resolveMember(state, args[0] || "lead");
|
|
17
21
|
if (!member) {
|
|
18
|
-
console.log(
|
|
22
|
+
console.log(
|
|
23
|
+
`\n 사용법: ${WHITE}tfx multi interrupt <lead|이름|번호>${RESET}\n`,
|
|
24
|
+
);
|
|
19
25
|
return;
|
|
20
26
|
}
|
|
21
27
|
if (isWtMode(state)) {
|
|
22
|
-
console.log(
|
|
28
|
+
console.log(
|
|
29
|
+
`\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`,
|
|
30
|
+
);
|
|
23
31
|
return;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
if (isNativeMode(state)) {
|
|
27
|
-
const result = await nativeRequest(state, "/interrupt", {
|
|
28
|
-
|
|
35
|
+
const result = await nativeRequest(state, "/interrupt", {
|
|
36
|
+
member: member.name,
|
|
37
|
+
});
|
|
38
|
+
(result?.ok ? ok : warn)(
|
|
39
|
+
`${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`,
|
|
40
|
+
);
|
|
29
41
|
console.log("");
|
|
30
42
|
return;
|
|
31
43
|
}
|
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
import { closeWtSession, killSession, listSessions } from "../../session.mjs";
|
|
2
2
|
import { DIM, RESET } from "../../shared.mjs";
|
|
3
|
+
import { ok } from "../render.mjs";
|
|
3
4
|
import { nativeRequest } from "../services/native-control.mjs";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
isNativeMode,
|
|
7
|
+
isTeamAlive,
|
|
8
|
+
isWtMode,
|
|
9
|
+
} from "../services/runtime-mode.mjs";
|
|
5
10
|
import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
|
|
6
|
-
import { ok } from "../render.mjs";
|
|
7
11
|
|
|
8
12
|
export async function teamKill() {
|
|
9
13
|
const state = loadTeamState();
|
|
10
14
|
if (state && isNativeMode(state) && isTeamAlive(state)) {
|
|
11
15
|
await nativeRequest(state, "/stop", {});
|
|
12
|
-
try {
|
|
16
|
+
try {
|
|
17
|
+
process.kill(state.native.supervisorPid, "SIGTERM");
|
|
18
|
+
} catch {}
|
|
13
19
|
clearTeamState(state.sessionId);
|
|
14
20
|
ok(`종료: ${state.sessionName}`);
|
|
15
21
|
console.log("");
|
|
16
22
|
return;
|
|
17
23
|
}
|
|
18
24
|
if (state && isWtMode(state)) {
|
|
19
|
-
const closed = closeWtSession({
|
|
25
|
+
const closed = closeWtSession({
|
|
26
|
+
layout: state?.wt?.layout || state?.layout || "1xN",
|
|
27
|
+
paneCount: state?.wt?.paneCount ?? (state.members || []).length,
|
|
28
|
+
});
|
|
20
29
|
clearTeamState(state.sessionId);
|
|
21
|
-
ok(
|
|
30
|
+
ok(
|
|
31
|
+
`종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
|
|
32
|
+
);
|
|
22
33
|
console.log("");
|
|
23
34
|
return;
|
|
24
35
|
}
|