@triflux/remote 10.50.2 → 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/cto/brief.mjs +0 -20
- package/cto/collect.mjs +1 -200
- package/cto/current.schema.json +2 -23
- package/cto/hygiene.mjs +2 -61
- package/cto/lake-root.mjs +0 -56
- package/cto/status.mjs +5 -187
- package/hub/index.mjs +0 -17
- package/hub/team/agent-map.json +0 -1
- package/hub/team/agents-row-attach.py +120 -0
- package/hub/team/agents-row.mjs +200 -0
- package/hub/team/ansi.mjs +1 -35
- package/hub/team/backend.mjs +9 -145
- package/hub/team/claude-daemon-control.mjs +1 -2
- package/hub/team/claude-session-projection.mjs +5 -73
- package/hub/team/cli/commands/attach.mjs +1 -17
- package/hub/team/cli/commands/control.mjs +12 -38
- package/hub/team/cli/commands/debug.mjs +2 -39
- package/hub/team/cli/commands/focus.mjs +5 -31
- package/hub/team/cli/commands/interrupt.mjs +6 -25
- package/hub/team/cli/commands/kill.mjs +1 -30
- package/hub/team/cli/commands/list.mjs +0 -16
- package/hub/team/cli/commands/send.mjs +6 -26
- package/hub/team/cli/commands/start/index.mjs +42 -133
- package/hub/team/cli/commands/start/parse-args.mjs +15 -40
- package/hub/team/cli/commands/start/start-headless.mjs +138 -105
- package/hub/team/cli/commands/start/start-mux.mjs +2 -7
- package/hub/team/cli/commands/status.mjs +2 -57
- package/hub/team/cli/commands/stop.mjs +14 -19
- package/hub/team/cli/help.mjs +4 -5
- package/hub/team/cli/manifest.mjs +5 -1
- package/hub/team/cli/render.mjs +0 -12
- package/hub/team/cli/services/runtime-mode.mjs +27 -98
- package/hub/team/cli/services/state-store.mjs +5 -20
- package/hub/team/handoff.mjs +3 -34
- package/hub/team/headless.mjs +108 -1296
- package/hub/team/orchestrator.mjs +4 -30
- package/hub/team/pane.mjs +6 -28
- package/hub/team/psmux.mjs +3 -34
- package/hub/team/remote-session.mjs +1 -2
- package/hub/team/session-context.mjs +1 -1
- package/hub/team/session.mjs +7 -91
- package/hub/team/tui-core.mjs +1 -1
- package/hub/team/tui-viewer.mjs +11 -58
- package/hub/team/tui.mjs +8 -306
- package/hub/team/uds-orchestrator.mjs +2 -580
- package/hub/team/wt-manager.mjs +2 -2
- package/hub/workers/factory.mjs +6 -84
- package/hub/workers/interface.mjs +1 -7
- package/hub/workers/lib/jsonrpc-core.mjs +11 -0
- package/hub/workers/lib/jsonrpc-ws-uds.mjs +5 -8
- package/package.json +3 -8
- package/scripts/lib/agent-route-policy.mjs +143 -62
- package/scripts/lib/cli-claude.mjs +0 -14
- package/scripts/lib/env-probe.mjs +2 -131
- package/scripts/lib/gemini-profiles.mjs +2 -69
- package/scripts/lib/legacy-mcp-cleanup.mjs +283 -0
- package/scripts/lib/machine-profile.mjs +3 -17
- package/scripts/lib/mcp-filter.mjs +109 -53
- package/scripts/lib/mcp-guard-engine.mjs +40 -98
- package/scripts/lib/mcp-health.mjs +2 -2
- package/scripts/lib/mcp-server-catalog.mjs +2 -2
- package/scripts/lib/psmux-info.mjs +11 -59
- package/scripts/lib/skill-template.mjs +0 -245
- package/hub/hub-lifecycle.mjs +0 -248
- package/hub/pipe.mjs +0 -825
- package/hub/promote-penalties.mjs +0 -138
- package/hub/public/dashboard.html +0 -416
- package/hub/schema.sql +0 -209
- package/hub/server.mjs +0 -3048
- package/hub/store-adapter.mjs +0 -182
- package/hub/store.mjs +0 -1736
- package/hub/team/build-worker-prompt.mjs +0 -92
- package/hub/team/check-mcp-hub.mjs +0 -49
- package/hub/team/claude-native-bridge.mjs +0 -987
- package/hub/team/cli/commands/start/start-in-process.mjs +0 -50
- package/hub/team/cli/commands/start/start-wt.mjs +0 -86
- package/hub/team/cli/services/hub-client.mjs +0 -264
- package/hub/team/cli/services/native-control.mjs +0 -145
- package/hub/team/codex-review.mjs +0 -520
- package/hub/team/conductor-mesh-bridge.mjs +0 -122
- package/hub/team/conductor-registry.mjs +0 -121
- package/hub/team/conductor.mjs +0 -1529
- package/hub/team/consensus-meta.mjs +0 -176
- package/hub/team/cto-auto-collect.mjs +0 -115
- package/hub/team/cto-pull.mjs +0 -31
- package/hub/team/daemon-pty-tmux-bridge.mjs +0 -146
- package/hub/team/dashboard.mjs +0 -322
- package/hub/team/event-log.mjs +0 -84
- package/hub/team/execution-mode.mjs +0 -324
- package/hub/team/extract-completion-payload.mjs +0 -110
- package/hub/team/headless-bridge-session.mjs +0 -17
- package/hub/team/health-probe.mjs +0 -435
- package/hub/team/interactive-native-launcher.mjs +0 -119
- package/hub/team/interactive-native-worker.mjs +0 -80
- package/hub/team/interactive-tui-transport.mjs +0 -479
- package/hub/team/launcher-template.mjs +0 -144
- package/hub/team/lead-control.mjs +0 -191
- package/hub/team/mcp-selector.mjs +0 -145
- package/hub/team/native-supervisor.mjs +0 -460
- package/hub/team/native.mjs +0 -727
- package/hub/team/recovery-store.mjs +0 -149
- package/hub/team/remote-probe.mjs +0 -292
- package/hub/team/remote-watcher.mjs +0 -506
- package/hub/team/runtime-strategy.mjs +0 -200
- package/hub/team/schemas/worker-completion.json +0 -39
- package/hub/team/sentinel-capture.mjs +0 -127
- package/hub/team/session-sync.mjs +0 -316
- package/hub/team/swarm-cli.mjs +0 -390
- package/hub/team/swarm-hypervisor.mjs +0 -2206
- package/hub/team/swarm-intent.mjs +0 -216
- package/hub/team/swarm-locks.mjs +0 -335
- package/hub/team/swarm-planner.mjs +0 -371
- package/hub/team/swarm-preflight.mjs +0 -366
- package/hub/team/swarm-reconciler.mjs +0 -242
- package/hub/team/synapse-cli.mjs +0 -458
- package/hub/team/synapse-http.mjs +0 -311
- package/hub/team/synapse-registry.mjs +0 -618
- package/hub/team/tui-remote-adapter.mjs +0 -377
- package/hub/team/tui-synapse.mjs +0 -223
- package/hub/team/worker-completion-validator.mjs +0 -79
- package/hub/team/worker-sandbox.mjs +0 -125
- package/hub/team/worktree-lifecycle.mjs +0 -639
- package/hub/team/wt-templates.json +0 -43
- package/hub/tools.mjs +0 -900
- package/hub/workers/antigravity-route-worker.mjs +0 -179
- package/hub/workers/codex-app-server-worker.mjs +0 -1067
- package/hub/workers/delegator-mcp.mjs +0 -1098
- package/hub/workers/lib/jsonrpc-stdio.mjs +0 -204
- package/scripts/lib/claudemd-scanner.mjs +0 -223
- package/scripts/lib/context.mjs +0 -67
- package/scripts/lib/dynamic-route-cli.mjs +0 -107
- package/scripts/lib/handoff.mjs +0 -187
- package/scripts/lib/logger.mjs +0 -122
- package/scripts/lib/stealth-fetch.mjs +0 -176
package/hub/team/backend.mjs
CHANGED
|
@@ -1,153 +1,17 @@
|
|
|
1
|
-
// hub/team/backend.mjs — CLI 백엔드 추상화 레이어
|
|
2
|
-
// 각 CLI(codex/antigravity/claude)의 명령 빌드 로직을 클래스로 캡슐화한다.
|
|
3
|
-
// v7.2.2
|
|
4
|
-
import { writeFileSync } from "node:fs";
|
|
5
|
-
import { createRequire } from "node:module";
|
|
6
|
-
|
|
7
|
-
import { buildExecArgs } from "@triflux/core/hub/codex-adapter.mjs";
|
|
8
1
|
import { IS_WINDOWS } from "@triflux/core/hub/platform.mjs";
|
|
2
|
+
import { powershellSingleQuote, shellQuote } from "./terminal-opener.mjs";
|
|
9
3
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
prompt,
|
|
16
|
-
resultFile,
|
|
17
|
-
{ isWindows } = {},
|
|
18
|
-
) {
|
|
19
|
-
// Persist prompt to a sibling file so the shell never interpolates raw
|
|
20
|
-
// prompt content. Without this the Unix branch let `$()`, backticks, `\\`,
|
|
21
|
-
// and `"` reach the shell verbatim (P1: shell injection).
|
|
22
|
-
const promptFile = `${resultFile}.prompt`;
|
|
23
|
-
writeFileSync(promptFile, prompt);
|
|
24
|
-
// agy 1.1.27 부터 --print 는 값(프롬프트)이 필수이고 stdin 프롬프트는 거부된다.
|
|
25
|
-
// 파일 내용을 셸이 재해석하지 않는 형태(따옴표 안 명령 치환 / Get-Content 식)로 값에 넣는다.
|
|
26
|
-
if (isWindows) {
|
|
27
|
-
return `agy --dangerously-skip-permissions --print (Get-Content -Raw '${promptFile}') > '${resultFile}' 2>'${resultFile}.err'`;
|
|
28
|
-
}
|
|
29
|
-
return `agy --dangerously-skip-permissions --print "$(cat '${promptFile}')" > '${resultFile}' 2>'${resultFile}.err'`;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const _require = createRequire(import.meta.url);
|
|
33
|
-
|
|
34
|
-
// ── 백엔드 클래스 ──────────────────────────────────────────────────────────
|
|
35
|
-
|
|
36
|
-
export class CodexBackend {
|
|
37
|
-
name() {
|
|
38
|
-
return "codex";
|
|
39
|
-
}
|
|
40
|
-
command() {
|
|
41
|
-
return "codex";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* @param {string} prompt — 프롬프트 (프롬프트 파일 경로가 아닌 PowerShell 표현식)
|
|
46
|
-
* @param {string} resultFile — 결과 저장 경로
|
|
47
|
-
* @param {object} [opts]
|
|
48
|
-
* @returns {string} PowerShell 명령 (cls 제외)
|
|
49
|
-
*/
|
|
50
|
-
buildArgs(prompt, resultFile, opts = {}) {
|
|
51
|
-
return buildExecArgs({ prompt, resultFile, ...opts });
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
env() {
|
|
55
|
-
return {};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export class GeminiBackend {
|
|
60
|
-
name() {
|
|
61
|
-
return "gemini";
|
|
62
|
-
}
|
|
63
|
-
command() {
|
|
64
|
-
return "agy";
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
buildArgs(prompt, resultFile, opts = {}) {
|
|
68
|
-
return buildGeminiCommand(prompt, resultFile, { isWindows: IS_WINDOWS });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
env() {
|
|
72
|
-
return {};
|
|
4
|
+
class ClaudeBackend {
|
|
5
|
+
buildArgs(prompt, resultFile, { isWindows = IS_WINDOWS } = {}) {
|
|
6
|
+
const quote = isWindows ? powershellSingleQuote : shellQuote;
|
|
7
|
+
const args = ["--print", prompt, "--output-format", "text"];
|
|
8
|
+
return `claude ${args.map(quote).join(" ")} > ${quote(resultFile)} 2>&1`;
|
|
73
9
|
}
|
|
74
10
|
}
|
|
75
11
|
|
|
76
|
-
|
|
77
|
-
name() {
|
|
78
|
-
return "claude";
|
|
79
|
-
}
|
|
80
|
-
command() {
|
|
81
|
-
return "claude";
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
buildArgs(prompt, resultFile, opts = {}) {
|
|
85
|
-
return `claude --print ${prompt} --output-format text > '${resultFile}' 2>&1`;
|
|
86
|
-
}
|
|
12
|
+
const claude = new ClaudeBackend();
|
|
87
13
|
|
|
88
|
-
env() {
|
|
89
|
-
return {};
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
export class AntigravityBackend {
|
|
94
|
-
name() {
|
|
95
|
-
return "antigravity";
|
|
96
|
-
}
|
|
97
|
-
command() {
|
|
98
|
-
return "agy";
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
buildArgs(prompt, resultFile, opts = {}) {
|
|
102
|
-
return buildAntigravityCommand(prompt, resultFile, {
|
|
103
|
-
isWindows: IS_WINDOWS,
|
|
104
|
-
...opts,
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
env() {
|
|
109
|
-
return {};
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// ── 레지스트리 ─────────────────────────────────────────────────────────────
|
|
114
|
-
|
|
115
|
-
/** @type {Map<string, CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend>} */
|
|
116
|
-
const backends = new Map([
|
|
117
|
-
["codex", new CodexBackend()],
|
|
118
|
-
["gemini", new GeminiBackend()],
|
|
119
|
-
["claude", new ClaudeBackend()],
|
|
120
|
-
["antigravity", new AntigravityBackend()],
|
|
121
|
-
]);
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* 백엔드 이름으로 조회한다.
|
|
125
|
-
* @param {string} name — "codex" | "gemini" | "claude" | "antigravity"
|
|
126
|
-
* @returns {CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend}
|
|
127
|
-
* @throws {Error} 등록되지 않은 이름
|
|
128
|
-
*/
|
|
129
14
|
export function getBackend(name) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
return b;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* 에이전트명 또는 CLI명을 Backend로 해석한다.
|
|
137
|
-
* agent-map.json을 통해 에이전트명 → CLI명으로 변환 후 레지스트리에서 조회한다.
|
|
138
|
-
* @param {string} agentOrCli — "executor", "codex", "designer" 등
|
|
139
|
-
* @returns {CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend}
|
|
140
|
-
*/
|
|
141
|
-
export function getBackendForAgent(agentOrCli) {
|
|
142
|
-
const agentMap = _require("./agent-map.json");
|
|
143
|
-
const cliName = agentMap[agentOrCli] || agentOrCli;
|
|
144
|
-
return getBackend(cliName);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* 등록된 모든 백엔드를 반환한다.
|
|
149
|
-
* @returns {Array<CodexBackend|GeminiBackend|ClaudeBackend|AntigravityBackend>}
|
|
150
|
-
*/
|
|
151
|
-
export function listBackends() {
|
|
152
|
-
return Array.from(backends.values());
|
|
15
|
+
if (name !== "claude") throw new Error(`지원하지 않는 CLI: ${name}`);
|
|
16
|
+
return claude;
|
|
153
17
|
}
|
|
@@ -1775,8 +1775,7 @@ export async function dispatchClaudeDaemonJob({
|
|
|
1775
1775
|
/**
|
|
1776
1776
|
* dispatchClaudeDaemonJob 의 대칭 정리 경로.
|
|
1777
1777
|
* removeProjection + killDaemonJob (+ optional removeClaudeJobState) 를
|
|
1778
|
-
* 모두 시도하며, 각 스텝 오류는
|
|
1779
|
-
* 동일하게 .catch 로 삼킨다.
|
|
1778
|
+
* 모두 시도하며, 각 스텝 오류는 .catch 로 삼킨다.
|
|
1780
1779
|
*/
|
|
1781
1780
|
export async function teardownClaudeDaemonJob({
|
|
1782
1781
|
controlSock,
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// 실제 bridge ID 가 없으면 만들어 내지 않는다. 추정 ID 는 붙을 수 있는 세션처럼 보이게 한다.
|
|
5
|
+
export function normalizeClaudeBridgeSessionId(bridgeSessionId) {
|
|
5
6
|
const value = String(bridgeSessionId || "").trim();
|
|
6
|
-
if (value.startsWith("session_")) return value;
|
|
7
7
|
if (value.startsWith("cse_")) return `session_${value.slice(4)}`;
|
|
8
|
-
|
|
9
|
-
return `session_${short}`;
|
|
8
|
+
return value;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
export function buildClaudeSessionProjection({
|
|
@@ -30,6 +29,7 @@ export function buildClaudeSessionProjection({
|
|
|
30
29
|
if (!cwd) throw new Error("cwd is required");
|
|
31
30
|
if (!name) throw new Error("name is required");
|
|
32
31
|
if (!agent) throw new Error("agent is required");
|
|
32
|
+
const bridge = normalizeClaudeBridgeSessionId(bridgeSessionId);
|
|
33
33
|
return {
|
|
34
34
|
pid,
|
|
35
35
|
sessionId,
|
|
@@ -45,7 +45,7 @@ export function buildClaudeSessionProjection({
|
|
|
45
45
|
jobId: short,
|
|
46
46
|
status,
|
|
47
47
|
updatedAt,
|
|
48
|
-
bridgeSessionId:
|
|
48
|
+
...(bridge ? { bridgeSessionId: bridge } : {}),
|
|
49
49
|
};
|
|
50
50
|
}
|
|
51
51
|
|
|
@@ -58,74 +58,6 @@ export async function writeClaudeSessionProjection(sessionsDir, projection) {
|
|
|
58
58
|
return sessionPath;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export async function updateClaudeSessionProjection(sessionPath, patch) {
|
|
62
|
-
const parsed = JSON.parse(await fs.readFile(sessionPath, "utf8"));
|
|
63
|
-
const next = {
|
|
64
|
-
...parsed,
|
|
65
|
-
...patch,
|
|
66
|
-
updatedAt: patch.updatedAt || Date.now(),
|
|
67
|
-
};
|
|
68
|
-
await fs.writeFile(sessionPath, `${JSON.stringify(next)}\n`, "utf8");
|
|
69
|
-
return next;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
function projectionSessionId(projection) {
|
|
73
|
-
return String(
|
|
74
|
-
projection?.sessionId ?? projection?.session_id ?? projection?.id ?? "",
|
|
75
|
-
).trim();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export async function findClaudeSessionProjectionBySessionId(
|
|
79
|
-
sessionsDir,
|
|
80
|
-
sessionId,
|
|
81
|
-
) {
|
|
82
|
-
const expected = String(sessionId || "").trim();
|
|
83
|
-
if (!sessionsDir || !expected) return null;
|
|
84
|
-
let entries;
|
|
85
|
-
try {
|
|
86
|
-
entries = await fs.readdir(sessionsDir, { withFileTypes: true });
|
|
87
|
-
} catch (error) {
|
|
88
|
-
if (error?.code === "ENOENT") return null;
|
|
89
|
-
throw error;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
for (const entry of entries) {
|
|
93
|
-
if (!entry.isFile() || !entry.name.endsWith(".json")) continue;
|
|
94
|
-
const filePath = path.join(sessionsDir, entry.name);
|
|
95
|
-
try {
|
|
96
|
-
const projection = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
97
|
-
if (projectionSessionId(projection) === expected) {
|
|
98
|
-
return { path: filePath, projection };
|
|
99
|
-
}
|
|
100
|
-
} catch (error) {
|
|
101
|
-
if (error instanceof SyntaxError) continue;
|
|
102
|
-
if (error?.code === "ENOENT") continue;
|
|
103
|
-
throw error;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return null;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export async function refreshClaudeSessionProjectionCwd({
|
|
110
|
-
sessionsDir,
|
|
111
|
-
sessionId,
|
|
112
|
-
cwd,
|
|
113
|
-
updatedAt = Date.now(),
|
|
114
|
-
} = {}) {
|
|
115
|
-
const nextCwd = String(cwd || "").trim();
|
|
116
|
-
if (!nextCwd) return { updated: false, reason: "missing_cwd" };
|
|
117
|
-
const found = await findClaudeSessionProjectionBySessionId(
|
|
118
|
-
sessionsDir,
|
|
119
|
-
sessionId,
|
|
120
|
-
);
|
|
121
|
-
if (!found) return { updated: false, reason: "projection_not_found" };
|
|
122
|
-
const projection = await updateClaudeSessionProjection(found.path, {
|
|
123
|
-
cwd: nextCwd,
|
|
124
|
-
updatedAt,
|
|
125
|
-
});
|
|
126
|
-
return { updated: true, path: found.path, projection };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
61
|
export async function removeClaudeSessionProjection(sessionPath) {
|
|
130
62
|
await fs.rm(sessionPath, { force: true });
|
|
131
63
|
}
|
|
@@ -6,11 +6,7 @@ import {
|
|
|
6
6
|
launchAttachInWindowsTerminal,
|
|
7
7
|
wantsWtAttachFallback,
|
|
8
8
|
} from "../services/attach-fallback.mjs";
|
|
9
|
-
import {
|
|
10
|
-
isNativeMode,
|
|
11
|
-
isTeamAlive,
|
|
12
|
-
isWtMode,
|
|
13
|
-
} from "../services/runtime-mode.mjs";
|
|
9
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
14
10
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
15
11
|
|
|
16
12
|
export async function teamAttach(args = []) {
|
|
@@ -19,18 +15,6 @@ export async function teamAttach(args = []) {
|
|
|
19
15
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
20
16
|
return;
|
|
21
17
|
}
|
|
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
18
|
|
|
35
19
|
try {
|
|
36
20
|
attachSession(state.sessionName);
|
|
@@ -1,18 +1,15 @@
|
|
|
1
1
|
import { injectPrompt, sendKeys } from "../../pane.mjs";
|
|
2
|
-
import { DIM, RESET, WHITE
|
|
3
|
-
import { ok
|
|
4
|
-
import { publishLeadControl } from "../services/hub-client.mjs";
|
|
2
|
+
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
+
import { ok } from "../render.mjs";
|
|
5
4
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
isNativeMode,
|
|
9
|
-
isTeamAlive,
|
|
10
|
-
isWtMode,
|
|
11
|
-
} from "../services/runtime-mode.mjs";
|
|
5
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
12
6
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
13
7
|
|
|
14
8
|
export async function teamControl(args = []) {
|
|
15
9
|
const state = loadTeamState();
|
|
10
|
+
if (state?.teammateMode === "headless") {
|
|
11
|
+
throw new Error("headless 실행에는 지원하지 않는다");
|
|
12
|
+
}
|
|
16
13
|
if (!state || !isTeamAlive(state)) {
|
|
17
14
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
18
15
|
return;
|
|
@@ -30,35 +27,12 @@ export async function teamControl(args = []) {
|
|
|
30
27
|
);
|
|
31
28
|
return;
|
|
32
29
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let directOk = false;
|
|
41
|
-
if (isNativeMode(state)) {
|
|
42
|
-
directOk = !!(
|
|
43
|
-
await nativeRequest(state, "/control", {
|
|
44
|
-
member: member.name,
|
|
45
|
-
command,
|
|
46
|
-
reason,
|
|
47
|
-
})
|
|
48
|
-
)?.ok;
|
|
49
|
-
} else {
|
|
50
|
-
injectPrompt(
|
|
51
|
-
member.pane,
|
|
52
|
-
`[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
|
|
53
|
-
);
|
|
54
|
-
if (command === "interrupt") sendKeys(member.pane, "C-c");
|
|
55
|
-
directOk = true;
|
|
56
|
-
}
|
|
30
|
+
injectPrompt(
|
|
31
|
+
member.pane,
|
|
32
|
+
`[LEAD CONTROL] command=${command}${reason ? ` reason=${reason}` : ""}`,
|
|
33
|
+
);
|
|
34
|
+
if (command === "interrupt") sendKeys(member.pane, "C-c");
|
|
57
35
|
|
|
58
|
-
|
|
59
|
-
if (directOk && published)
|
|
60
|
-
ok(`${member.name} 제어 전송 (${command}, direct + hub)`);
|
|
61
|
-
else if (directOk) ok(`${member.name} 제어 전송 (${command}, direct only)`);
|
|
62
|
-
else warn(`${member.name} 제어 전송 실패 (${command})`);
|
|
36
|
+
ok(`${member.name} 제어 전송 (${command})`);
|
|
63
37
|
console.log("");
|
|
64
38
|
}
|
|
@@ -2,19 +2,11 @@ import {
|
|
|
2
2
|
capturePaneOutput,
|
|
3
3
|
detectMultiplexer,
|
|
4
4
|
getSessionAttachedCount,
|
|
5
|
-
hasWindowsTerminal,
|
|
6
|
-
hasWindowsTerminalSession,
|
|
7
5
|
listSessions,
|
|
8
6
|
} from "../../session.mjs";
|
|
9
7
|
import { AMBER, BOLD, DIM, RESET } from "../../shared.mjs";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
isNativeMode,
|
|
14
|
-
isTeamAlive,
|
|
15
|
-
isWtMode,
|
|
16
|
-
} from "../services/runtime-mode.mjs";
|
|
17
|
-
import { loadTeamState, TEAM_PROFILE } from "../services/state-store.mjs";
|
|
8
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
9
|
+
import { loadTeamState } from "../services/state-store.mjs";
|
|
18
10
|
|
|
19
11
|
export async function teamDebug(args = []) {
|
|
20
12
|
const state = loadTeamState();
|
|
@@ -23,7 +15,6 @@ export async function teamDebug(args = []) {
|
|
|
23
15
|
flagIndex === -1
|
|
24
16
|
? 20
|
|
25
17
|
: Math.max(3, parseInt(args[flagIndex + 1] || "20", 10) || 20);
|
|
26
|
-
const hub = await getHubInfo();
|
|
27
18
|
|
|
28
19
|
console.log(`\n ${AMBER}${BOLD}⬡ Team Debug${RESET}\n`);
|
|
29
20
|
console.log(` platform: ${process.platform}`);
|
|
@@ -32,8 +23,6 @@ export async function teamDebug(args = []) {
|
|
|
32
23
|
` tty: stdout=${!!process.stdout.isTTY}, stdin=${!!process.stdin.isTTY}`,
|
|
33
24
|
);
|
|
34
25
|
console.log(` mux: ${detectMultiplexer() || "none"}`);
|
|
35
|
-
console.log(` hub-pid: ${hub ? `${hub.pid}` : "-"}`);
|
|
36
|
-
console.log(` hub-url: ${hub?.url || "-"}`);
|
|
37
26
|
const sessions = listSessions();
|
|
38
27
|
console.log(` sessions: ${sessions.length ? sessions.join(", ") : "-"}`);
|
|
39
28
|
|
|
@@ -44,7 +33,6 @@ export async function teamDebug(args = []) {
|
|
|
44
33
|
|
|
45
34
|
console.log(`\n ${BOLD}state${RESET}`);
|
|
46
35
|
console.log(` session: ${state.sessionName}`);
|
|
47
|
-
console.log(` profile: ${state.profile || TEAM_PROFILE}`);
|
|
48
36
|
console.log(` mode: ${state.teammateMode || "tmux"}`);
|
|
49
37
|
console.log(` lead: ${state.lead}`);
|
|
50
38
|
console.log(` agents: ${(state.agents || []).join(", ")}`);
|
|
@@ -53,31 +41,6 @@ export async function teamDebug(args = []) {
|
|
|
53
41
|
` attached: ${getSessionAttachedCount(state.sessionName) ?? "-"}`,
|
|
54
42
|
);
|
|
55
43
|
|
|
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
44
|
console.log(
|
|
82
45
|
`\n ${BOLD}pane-tail${RESET} ${DIM}(last ${lines} lines)${RESET}`,
|
|
83
46
|
);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { attachSession, focusPane
|
|
1
|
+
import { attachSession, focusPane } from "../../session.mjs";
|
|
2
2
|
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
3
|
import { fail, ok, warn } from "../render.mjs";
|
|
4
4
|
import {
|
|
@@ -7,25 +7,18 @@ import {
|
|
|
7
7
|
wantsWtAttachFallback,
|
|
8
8
|
} from "../services/attach-fallback.mjs";
|
|
9
9
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
10
|
-
import {
|
|
11
|
-
isNativeMode,
|
|
12
|
-
isTeamAlive,
|
|
13
|
-
isWtMode,
|
|
14
|
-
} from "../services/runtime-mode.mjs";
|
|
10
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
15
11
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
16
12
|
|
|
17
13
|
export async function teamFocus(args = []) {
|
|
18
14
|
const state = loadTeamState();
|
|
15
|
+
if (state?.teammateMode === "headless") {
|
|
16
|
+
throw new Error("headless 실행에는 지원하지 않는다");
|
|
17
|
+
}
|
|
19
18
|
if (!state || !isTeamAlive(state)) {
|
|
20
19
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
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
22
|
|
|
30
23
|
const member = resolveMember(state, args[0]);
|
|
31
24
|
if (!member) {
|
|
@@ -35,25 +28,6 @@ export async function teamFocus(args = []) {
|
|
|
35
28
|
return;
|
|
36
29
|
}
|
|
37
30
|
|
|
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
31
|
focusPane(member.pane, { zoom: false });
|
|
58
32
|
try {
|
|
59
33
|
attachSession(state.sessionName);
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { sendKeys } from "../../pane.mjs";
|
|
2
|
-
import { DIM, RESET, WHITE
|
|
3
|
-
import { ok
|
|
2
|
+
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
+
import { ok } from "../render.mjs";
|
|
4
4
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
isNativeMode,
|
|
8
|
-
isTeamAlive,
|
|
9
|
-
isWtMode,
|
|
10
|
-
} from "../services/runtime-mode.mjs";
|
|
5
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
11
6
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
12
7
|
|
|
13
8
|
export async function teamInterrupt(args = []) {
|
|
14
9
|
const state = loadTeamState();
|
|
10
|
+
if (state?.teammateMode === "headless") {
|
|
11
|
+
throw new Error("headless 실행에는 지원하지 않는다");
|
|
12
|
+
}
|
|
15
13
|
if (!state || !isTeamAlive(state)) {
|
|
16
14
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
17
15
|
return;
|
|
@@ -24,23 +22,6 @@ export async function teamInterrupt(args = []) {
|
|
|
24
22
|
);
|
|
25
23
|
return;
|
|
26
24
|
}
|
|
27
|
-
if (isWtMode(state)) {
|
|
28
|
-
console.log(
|
|
29
|
-
`\n ${YELLOW}⚠${RESET} wt 모드에서는 pane stdin 주입이 지원되지 않아 interrupt를 자동 전송할 수 없습니다.\n ${DIM}수동으로 해당 pane에서 Ctrl+C를 입력하세요.${RESET}\n`,
|
|
30
|
-
);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (isNativeMode(state)) {
|
|
35
|
-
const result = await nativeRequest(state, "/interrupt", {
|
|
36
|
-
member: member.name,
|
|
37
|
-
});
|
|
38
|
-
(result?.ok ? ok : warn)(
|
|
39
|
-
`${member.name} ${result?.ok ? "인터럽트 전송" : "인터럽트 실패"}`,
|
|
40
|
-
);
|
|
41
|
-
console.log("");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
25
|
|
|
45
26
|
sendKeys(member.pane, "C-c");
|
|
46
27
|
ok(`${member.name} 인터럽트 전송`);
|
|
@@ -1,39 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { killSession, listSessions } from "../../session.mjs";
|
|
2
2
|
import { DIM, RESET } from "../../shared.mjs";
|
|
3
3
|
import { ok } from "../render.mjs";
|
|
4
|
-
import { nativeRequest } from "../services/native-control.mjs";
|
|
5
|
-
import {
|
|
6
|
-
isNativeMode,
|
|
7
|
-
isTeamAlive,
|
|
8
|
-
isWtMode,
|
|
9
|
-
} from "../services/runtime-mode.mjs";
|
|
10
4
|
import { clearTeamState, loadTeamState } from "../services/state-store.mjs";
|
|
11
5
|
|
|
12
6
|
export async function teamKill() {
|
|
13
7
|
const state = loadTeamState();
|
|
14
|
-
if (state && isNativeMode(state) && isTeamAlive(state)) {
|
|
15
|
-
await nativeRequest(state, "/stop", {});
|
|
16
|
-
try {
|
|
17
|
-
process.kill(state.native.supervisorPid, "SIGTERM");
|
|
18
|
-
} catch {}
|
|
19
|
-
clearTeamState(state.sessionId);
|
|
20
|
-
ok(`종료: ${state.sessionName}`);
|
|
21
|
-
console.log("");
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (state && isWtMode(state)) {
|
|
25
|
-
const closed = closeWtSession({
|
|
26
|
-
layout: state?.wt?.layout || state?.layout || "1xN",
|
|
27
|
-
paneCount: state?.wt?.paneCount ?? (state.members || []).length,
|
|
28
|
-
});
|
|
29
|
-
clearTeamState(state.sessionId);
|
|
30
|
-
ok(
|
|
31
|
-
`종료: ${state.sessionName}${closed ? ` (${closed} panes closed)` : ""}`,
|
|
32
|
-
);
|
|
33
|
-
console.log("");
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
8
|
const sessions = listSessions();
|
|
38
9
|
if (!sessions.length) {
|
|
39
10
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
import { listSessions } from "../../session.mjs";
|
|
2
2
|
import { AMBER, BOLD, DIM, GREEN, RESET } from "../../shared.mjs";
|
|
3
|
-
import {
|
|
4
|
-
isNativeMode,
|
|
5
|
-
isTeamAlive,
|
|
6
|
-
isWtMode,
|
|
7
|
-
} from "../services/runtime-mode.mjs";
|
|
8
|
-
import { loadTeamState } from "../services/state-store.mjs";
|
|
9
3
|
|
|
10
4
|
export function teamList() {
|
|
11
|
-
const state = loadTeamState();
|
|
12
|
-
if (state && isTeamAlive(state) && (isNativeMode(state) || isWtMode(state))) {
|
|
13
|
-
console.log(`\n ${AMBER}${BOLD}⬡ 팀 세션 목록${RESET}\n`);
|
|
14
|
-
console.log(
|
|
15
|
-
` ${GREEN}●${RESET} ${state.sessionName} ${DIM}(${isNativeMode(state) ? "in-process" : "wt"})${RESET}`,
|
|
16
|
-
);
|
|
17
|
-
console.log("");
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
5
|
const sessions = listSessions();
|
|
22
6
|
if (!sessions.length) {
|
|
23
7
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { injectPrompt } from "../../pane.mjs";
|
|
2
|
-
import { DIM, RESET, WHITE
|
|
3
|
-
import { ok
|
|
2
|
+
import { DIM, RESET, WHITE } from "../../shared.mjs";
|
|
3
|
+
import { ok } from "../render.mjs";
|
|
4
4
|
import { resolveMember } from "../services/member-selector.mjs";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
isNativeMode,
|
|
8
|
-
isTeamAlive,
|
|
9
|
-
isWtMode,
|
|
10
|
-
} from "../services/runtime-mode.mjs";
|
|
5
|
+
import { isTeamAlive } from "../services/runtime-mode.mjs";
|
|
11
6
|
import { loadTeamState } from "../services/state-store.mjs";
|
|
12
7
|
|
|
13
8
|
export async function teamSend(args = []) {
|
|
14
9
|
const state = loadTeamState();
|
|
10
|
+
if (state?.teammateMode === "headless") {
|
|
11
|
+
throw new Error("headless 실행에는 지원하지 않는다");
|
|
12
|
+
}
|
|
15
13
|
if (!state || !isTeamAlive(state)) {
|
|
16
14
|
console.log(`\n ${DIM}활성 팀 세션 없음${RESET}\n`);
|
|
17
15
|
return;
|
|
@@ -25,24 +23,6 @@ export async function teamSend(args = []) {
|
|
|
25
23
|
);
|
|
26
24
|
return;
|
|
27
25
|
}
|
|
28
|
-
if (isWtMode(state)) {
|
|
29
|
-
console.log(
|
|
30
|
-
`\n ${YELLOW}⚠${RESET} wt 모드는 pane 프롬프트 자동 주입(send)이 지원되지 않습니다.\n ${DIM}수동 전달: 선택한 pane에 직접 붙여넣으세요.${RESET}\n`,
|
|
31
|
-
);
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (isNativeMode(state)) {
|
|
36
|
-
const result = await nativeRequest(state, "/send", {
|
|
37
|
-
member: member.name,
|
|
38
|
-
text: message,
|
|
39
|
-
});
|
|
40
|
-
(result?.ok ? ok : warn)(
|
|
41
|
-
`${member.name}${result?.ok ? "에 메시지 주입 완료" : " 메시지 주입 실패"}`,
|
|
42
|
-
);
|
|
43
|
-
console.log("");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
26
|
|
|
47
27
|
injectPrompt(member.pane, message);
|
|
48
28
|
ok(`${member.name}에 메시지 주입 완료`);
|