@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
package/hub/team/wt-manager.mjs
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
renameSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
3
9
|
import { platform as osPlatform, tmpdir } from "node:os";
|
|
4
10
|
import { join } from "node:path";
|
|
5
|
-
|
|
11
|
+
import { getEnvironment } from "@triflux/core/hub/lib/env-detect.mjs";
|
|
12
|
+
import * as childProcess from "@triflux/core/hub/lib/spawn-trace.mjs";
|
|
6
13
|
import { sendKeysToPane } from "./psmux.mjs";
|
|
7
14
|
|
|
8
15
|
const DEFAULT_WINDOW_NAME = "triflux";
|
|
@@ -61,8 +68,8 @@ function buildWrappedCommand(pidFile, command) {
|
|
|
61
68
|
* CreateProcess 이중 쿼팅 문제를 완전히 회피한다.
|
|
62
69
|
*/
|
|
63
70
|
function encodeForPowerShell(script) {
|
|
64
|
-
const buf = Buffer.from(script,
|
|
65
|
-
return buf.toString(
|
|
71
|
+
const buf = Buffer.from(script, "utf16le");
|
|
72
|
+
return buf.toString("base64");
|
|
66
73
|
}
|
|
67
74
|
|
|
68
75
|
function matchesTitlePattern(title, pattern) {
|
|
@@ -99,6 +106,60 @@ function defaultIsPidAlive(pid, execFileSyncFn = childProcess.execFileSync) {
|
|
|
99
106
|
}
|
|
100
107
|
}
|
|
101
108
|
|
|
109
|
+
/**
|
|
110
|
+
* WT 기본 프로필의 폰트 크기를 읽는다.
|
|
111
|
+
*/
|
|
112
|
+
function getWtDefaultFontSize() {
|
|
113
|
+
const settingsPaths = [
|
|
114
|
+
join(
|
|
115
|
+
process.env.LOCALAPPDATA || "",
|
|
116
|
+
"Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState/settings.json",
|
|
117
|
+
),
|
|
118
|
+
join(
|
|
119
|
+
process.env.LOCALAPPDATA || "",
|
|
120
|
+
"Microsoft/Windows Terminal/settings.json",
|
|
121
|
+
),
|
|
122
|
+
];
|
|
123
|
+
for (const p of settingsPaths) {
|
|
124
|
+
if (!existsSync(p)) continue;
|
|
125
|
+
try {
|
|
126
|
+
const settings = JSON.parse(
|
|
127
|
+
readFileSync(p, "utf8").replace(/^\s*\/\/.*$/gm, ""),
|
|
128
|
+
);
|
|
129
|
+
const defaultGuid = settings.defaultProfile;
|
|
130
|
+
const profiles = settings.profiles?.list || [];
|
|
131
|
+
const defaultProfile =
|
|
132
|
+
profiles.find((pr) => pr.guid === defaultGuid) || profiles[0];
|
|
133
|
+
return (
|
|
134
|
+
defaultProfile?.font?.size ||
|
|
135
|
+
settings.profiles?.defaults?.font?.size ||
|
|
136
|
+
12
|
|
137
|
+
);
|
|
138
|
+
} catch {
|
|
139
|
+
/* 다음 */
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return 12;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* 파일을 원자적으로 쓴다.
|
|
147
|
+
*/
|
|
148
|
+
function atomicWriteSync(filePath, data) {
|
|
149
|
+
const tmpPath = `${filePath}.${process.pid}.tmp`;
|
|
150
|
+
try {
|
|
151
|
+
writeFileSync(tmpPath, data, "utf8");
|
|
152
|
+
renameSync(tmpPath, filePath);
|
|
153
|
+
} catch (err) {
|
|
154
|
+
try {
|
|
155
|
+
rmSync(tmpPath, { force: true });
|
|
156
|
+
} catch {
|
|
157
|
+
/* 무시 */
|
|
158
|
+
}
|
|
159
|
+
throw err;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
102
163
|
/**
|
|
103
164
|
* Windows Terminal 탭/세션 라이프사이클 관리자.
|
|
104
165
|
*
|
|
@@ -109,12 +170,34 @@ function defaultIsPidAlive(pid, execFileSyncFn = childProcess.execFileSync) {
|
|
|
109
170
|
* @param {number} [opts.tabCreateDelayMs=500]
|
|
110
171
|
* @param {object} [opts.deps] — 테스트용 의존성 주입
|
|
111
172
|
*/
|
|
173
|
+
function createNonWindowsStubManager() {
|
|
174
|
+
const asyncFalse = async () => false;
|
|
175
|
+
return Object.freeze({
|
|
176
|
+
ensureWtProfile: () => {},
|
|
177
|
+
createTab: asyncFalse,
|
|
178
|
+
renameTab: asyncFalse,
|
|
179
|
+
closeTab: asyncFalse,
|
|
180
|
+
listTabs: async () => [],
|
|
181
|
+
closeStale: async () => 0,
|
|
182
|
+
createSession: asyncFalse,
|
|
183
|
+
splitPane: asyncFalse,
|
|
184
|
+
applySplitLayout: asyncFalse,
|
|
185
|
+
getEnvironmentInfo: () => ({
|
|
186
|
+
platform: process.platform,
|
|
187
|
+
hasWindowsTerminal: false,
|
|
188
|
+
hasWt: false,
|
|
189
|
+
isWindowsTerminalSession: false,
|
|
190
|
+
}),
|
|
191
|
+
getTabCount: () => 0,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
112
195
|
export function createWtManager(opts = {}) {
|
|
113
196
|
const deps = opts.deps || {};
|
|
114
197
|
const platform = deps.platform || osPlatform;
|
|
115
198
|
|
|
116
199
|
if (platform() !== "win32") {
|
|
117
|
-
|
|
200
|
+
return createNonWindowsStubManager();
|
|
118
201
|
}
|
|
119
202
|
|
|
120
203
|
const now = deps.now || Date.now;
|
|
@@ -123,6 +206,7 @@ export function createWtManager(opts = {}) {
|
|
|
123
206
|
const execFileSyncFn = deps.execFileSync || childProcess.execFileSync;
|
|
124
207
|
const killFn = deps.kill || ((pid) => process.kill(pid));
|
|
125
208
|
const sendKeysFn = deps.sendKeysToPane || sendKeysToPane;
|
|
209
|
+
const getEnvironmentFn = deps.getEnvironment || getEnvironment;
|
|
126
210
|
const isPidAlive =
|
|
127
211
|
deps.isPidAlive || ((pid) => defaultIsPidAlive(pid, execFileSyncFn));
|
|
128
212
|
const ensureDir =
|
|
@@ -130,6 +214,7 @@ export function createWtManager(opts = {}) {
|
|
|
130
214
|
const exists = deps.exists || existsSync;
|
|
131
215
|
const readText =
|
|
132
216
|
deps.readText || ((filePath) => readFileSync(filePath, "utf8"));
|
|
217
|
+
const renameFile = deps.renameFile || renameSync;
|
|
133
218
|
const removeFile =
|
|
134
219
|
deps.removeFile || ((filePath) => rmSync(filePath, { force: true }));
|
|
135
220
|
|
|
@@ -146,12 +231,19 @@ export function createWtManager(opts = {}) {
|
|
|
146
231
|
opts.tabCreateDelayMs,
|
|
147
232
|
DEFAULT_TAB_CREATE_DELAY_MS,
|
|
148
233
|
) || DEFAULT_TAB_CREATE_DELAY_MS;
|
|
234
|
+
const _waitTimeoutMs =
|
|
235
|
+
resolvePositiveInteger(
|
|
236
|
+
opts.waitTimeoutMs,
|
|
237
|
+
process.env.WTM_WAIT_TIMEOUT_MS,
|
|
238
|
+
DEFAULT_WAIT_TIMEOUT_MS,
|
|
239
|
+
) || DEFAULT_WAIT_TIMEOUT_MS;
|
|
149
240
|
|
|
150
241
|
ensureDir(pidDir);
|
|
151
242
|
|
|
152
243
|
/** @type {Map<string, { pid: number, createdAt: number, pidFile: string }>} */
|
|
153
244
|
const tabs = new Map();
|
|
154
245
|
let lastTabCreateAt = null;
|
|
246
|
+
let _profileEnsured = false;
|
|
155
247
|
|
|
156
248
|
function forgetTab(title) {
|
|
157
249
|
const entry = tabs.get(title);
|
|
@@ -200,12 +292,95 @@ export function createWtManager(opts = {}) {
|
|
|
200
292
|
throw new Error(`WT tab ready timeout: ${title}`);
|
|
201
293
|
}
|
|
202
294
|
|
|
295
|
+
function ensureWtProfile(workerCount = 2) {
|
|
296
|
+
const settingsPaths = [
|
|
297
|
+
join(
|
|
298
|
+
process.env.LOCALAPPDATA || "",
|
|
299
|
+
"Packages/Microsoft.WindowsTerminal_8wekyb3d8bbwe/LocalState/settings.json",
|
|
300
|
+
),
|
|
301
|
+
join(
|
|
302
|
+
process.env.LOCALAPPDATA || "",
|
|
303
|
+
"Microsoft/Windows Terminal/settings.json",
|
|
304
|
+
),
|
|
305
|
+
];
|
|
306
|
+
|
|
307
|
+
for (const settingsPath of settingsPaths) {
|
|
308
|
+
if (!exists(settingsPath)) continue;
|
|
309
|
+
try {
|
|
310
|
+
const raw = readText(settingsPath);
|
|
311
|
+
const cleaned = raw.replace(/^\s*\/\/.*$/gm, "");
|
|
312
|
+
const settings = JSON.parse(cleaned);
|
|
313
|
+
if (!settings.profiles?.list) continue;
|
|
314
|
+
|
|
315
|
+
const existing = settings.profiles.list.findIndex(
|
|
316
|
+
(p) => p.name === "triflux",
|
|
317
|
+
);
|
|
318
|
+
const profile = {
|
|
319
|
+
name: "triflux",
|
|
320
|
+
commandline: "psmux",
|
|
321
|
+
icon: "\u{1F53A}",
|
|
322
|
+
tabTitle: "triflux",
|
|
323
|
+
suppressApplicationTitle: true,
|
|
324
|
+
opacity: 40,
|
|
325
|
+
useAcrylic: true,
|
|
326
|
+
unfocusedAppearance: { opacity: 20 },
|
|
327
|
+
colorScheme: "One Half Dark",
|
|
328
|
+
font: {
|
|
329
|
+
size: Math.max(
|
|
330
|
+
6,
|
|
331
|
+
getWtDefaultFontSize() - 1 - Math.floor(workerCount / 2),
|
|
332
|
+
),
|
|
333
|
+
},
|
|
334
|
+
closeOnExit: "always",
|
|
335
|
+
hidden: true,
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
if (existing >= 0) {
|
|
339
|
+
settings.profiles.list[existing] = {
|
|
340
|
+
...settings.profiles.list[existing],
|
|
341
|
+
...profile,
|
|
342
|
+
};
|
|
343
|
+
} else {
|
|
344
|
+
settings.profiles.list.push(profile);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
atomicWriteSync(settingsPath, JSON.stringify(settings, null, 2));
|
|
348
|
+
_profileEnsured = true;
|
|
349
|
+
return true;
|
|
350
|
+
} catch {
|
|
351
|
+
/* 파싱 실패 */
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
|
|
203
357
|
async function createTab(tab = {}) {
|
|
204
358
|
const title = String(tab.title || "").trim();
|
|
205
359
|
if (!title) {
|
|
206
360
|
throw new Error("title is required");
|
|
207
361
|
}
|
|
208
362
|
|
|
363
|
+
const env = getEnvironmentFn();
|
|
364
|
+
if (env?.terminal?.hasWt === false) {
|
|
365
|
+
return Object.freeze({
|
|
366
|
+
success: false,
|
|
367
|
+
reason: "wt-not-installed",
|
|
368
|
+
installHint: env.terminal.installHint || null,
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const shellPath = String(opts.profile || env?.shell?.path || "pwsh.exe");
|
|
373
|
+
|
|
374
|
+
// 프로필이 지정된 경우 WT settings.json에 존재하는지 보장
|
|
375
|
+
// headless가 workerCount 기반으로 이미 호출한 경우 _profileEnsured로 skip
|
|
376
|
+
if (tab.profile && !_profileEnsured) {
|
|
377
|
+
try {
|
|
378
|
+
ensureWtProfile();
|
|
379
|
+
} catch {
|
|
380
|
+
/* 프로필 보장 실패해도 진행 */
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
209
384
|
pruneDeadTabs();
|
|
210
385
|
|
|
211
386
|
if (tabs.has(title)) {
|
|
@@ -235,7 +410,7 @@ export function createWtManager(opts = {}) {
|
|
|
235
410
|
const script = buildWrappedCommand(pidFile, tab.command);
|
|
236
411
|
args.push(
|
|
237
412
|
"--",
|
|
238
|
-
|
|
413
|
+
shellPath,
|
|
239
414
|
"-NoExit",
|
|
240
415
|
"-EncodedCommand",
|
|
241
416
|
encodeForPowerShell(script),
|
|
@@ -255,6 +430,69 @@ export function createWtManager(opts = {}) {
|
|
|
255
430
|
pidFile,
|
|
256
431
|
});
|
|
257
432
|
tabs.set(title, entry);
|
|
433
|
+
return Object.freeze({
|
|
434
|
+
success: true,
|
|
435
|
+
title,
|
|
436
|
+
pid: entry.pid,
|
|
437
|
+
createdAt: entry.createdAt,
|
|
438
|
+
pidFile: entry.pidFile,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
function renameTab({ oldTitle, newTitle } = {}) {
|
|
443
|
+
const previousTitle = String(oldTitle || "").trim();
|
|
444
|
+
const nextTitle = String(newTitle || "").trim();
|
|
445
|
+
if (!previousTitle) {
|
|
446
|
+
throw new Error("oldTitle is required");
|
|
447
|
+
}
|
|
448
|
+
if (!nextTitle) {
|
|
449
|
+
throw new Error("newTitle is required");
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
pruneDeadTabs();
|
|
453
|
+
|
|
454
|
+
const entry = tabs.get(previousTitle);
|
|
455
|
+
if (!entry) {
|
|
456
|
+
return false;
|
|
457
|
+
}
|
|
458
|
+
if (previousTitle === nextTitle) {
|
|
459
|
+
return Object.freeze({
|
|
460
|
+
success: true,
|
|
461
|
+
title: nextTitle,
|
|
462
|
+
pid: entry.pid,
|
|
463
|
+
createdAt: entry.createdAt,
|
|
464
|
+
pidFile: entry.pidFile,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
if (tabs.has(nextTitle)) {
|
|
468
|
+
throw new Error(`WT tab already exists: ${nextTitle}`);
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const nextPidFile = buildPidFilePath(pidDir, nextTitle);
|
|
472
|
+
try {
|
|
473
|
+
removeFile(nextPidFile);
|
|
474
|
+
} catch {
|
|
475
|
+
/* ignore */
|
|
476
|
+
}
|
|
477
|
+
renameFile(entry.pidFile, nextPidFile);
|
|
478
|
+
|
|
479
|
+
tabs.delete(previousTitle);
|
|
480
|
+
const nextEntry = Object.freeze({
|
|
481
|
+
...entry,
|
|
482
|
+
pidFile: nextPidFile,
|
|
483
|
+
});
|
|
484
|
+
tabs.set(nextTitle, nextEntry);
|
|
485
|
+
return Object.freeze({
|
|
486
|
+
success: true,
|
|
487
|
+
title: nextTitle,
|
|
488
|
+
pid: nextEntry.pid,
|
|
489
|
+
createdAt: nextEntry.createdAt,
|
|
490
|
+
pidFile: nextEntry.pidFile,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function getEnvironmentInfo() {
|
|
495
|
+
return getEnvironmentFn();
|
|
258
496
|
}
|
|
259
497
|
|
|
260
498
|
async function closeTab(title) {
|
|
@@ -315,30 +553,47 @@ export function createWtManager(opts = {}) {
|
|
|
315
553
|
* @param {string} [opts.command] — pane에서 실행할 명령
|
|
316
554
|
* @param {number} [opts.size] — 퍼센트 (0-100)
|
|
317
555
|
*/
|
|
318
|
-
async function splitPane(
|
|
319
|
-
|
|
320
|
-
|
|
556
|
+
async function splitPane(splitOpts = {}) {
|
|
557
|
+
// 프로필이 지정된 경우 WT settings.json에 존재하는지 보장
|
|
558
|
+
if (splitOpts.profile && !_profileEnsured) {
|
|
559
|
+
try {
|
|
560
|
+
ensureWtProfile();
|
|
561
|
+
} catch {
|
|
562
|
+
/* 프로필 보장 실패해도 진행 */
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
const direction = splitOpts.direction === "H" ? "-H" : "-V";
|
|
567
|
+
const args = ["-w", windowName, "sp", direction];
|
|
321
568
|
|
|
322
|
-
if (
|
|
323
|
-
args.push(
|
|
569
|
+
if (splitOpts.title) {
|
|
570
|
+
args.push("--title", String(splitOpts.title));
|
|
324
571
|
}
|
|
325
|
-
if (
|
|
326
|
-
args.push(
|
|
572
|
+
if (splitOpts.profile) {
|
|
573
|
+
args.push("--profile", String(splitOpts.profile));
|
|
327
574
|
}
|
|
328
|
-
if (
|
|
329
|
-
args.push(
|
|
575
|
+
if (splitOpts.size && Number.isFinite(splitOpts.size)) {
|
|
576
|
+
args.push("-s", String(splitOpts.size / 100));
|
|
330
577
|
}
|
|
331
|
-
if (
|
|
332
|
-
args.push(
|
|
578
|
+
if (splitOpts.cwd) {
|
|
579
|
+
args.push("-d", String(splitOpts.cwd));
|
|
333
580
|
}
|
|
334
|
-
if (
|
|
335
|
-
const
|
|
336
|
-
|
|
581
|
+
if (splitOpts.command) {
|
|
582
|
+
const env = getEnvironmentFn();
|
|
583
|
+
const shellPath = String(opts.profile || env?.shell?.path || "pwsh.exe");
|
|
584
|
+
const script = splitOpts.command;
|
|
585
|
+
args.push(
|
|
586
|
+
"--",
|
|
587
|
+
shellPath,
|
|
588
|
+
"-NoExit",
|
|
589
|
+
"-EncodedCommand",
|
|
590
|
+
encodeForPowerShell(script),
|
|
591
|
+
);
|
|
337
592
|
}
|
|
338
593
|
|
|
339
|
-
const child = spawnFn(
|
|
594
|
+
const child = spawnFn("wt.exe", args, {
|
|
340
595
|
detached: true,
|
|
341
|
-
stdio:
|
|
596
|
+
stdio: "ignore",
|
|
342
597
|
});
|
|
343
598
|
child?.unref?.();
|
|
344
599
|
|
|
@@ -355,18 +610,27 @@ export function createWtManager(opts = {}) {
|
|
|
355
610
|
|
|
356
611
|
// 첫 번째는 새 탭으로 생성
|
|
357
612
|
const first = panes[0];
|
|
358
|
-
await createTab({
|
|
613
|
+
const createResult = await createTab({
|
|
614
|
+
title: first.title,
|
|
615
|
+
command: first.command,
|
|
616
|
+
profile: first.profile,
|
|
617
|
+
});
|
|
618
|
+
if (createResult?.success === false) {
|
|
619
|
+
return createResult;
|
|
620
|
+
}
|
|
359
621
|
|
|
360
622
|
// 나머지는 split-pane으로 분할
|
|
361
623
|
for (let i = 1; i < panes.length; i++) {
|
|
362
624
|
await splitPane({
|
|
363
|
-
direction: panes[i].direction ||
|
|
625
|
+
direction: panes[i].direction || "V",
|
|
364
626
|
title: panes[i].title,
|
|
365
627
|
command: panes[i].command,
|
|
366
628
|
profile: panes[i].profile,
|
|
367
629
|
size: panes[i].size,
|
|
368
630
|
});
|
|
369
631
|
}
|
|
632
|
+
|
|
633
|
+
return createResult;
|
|
370
634
|
}
|
|
371
635
|
|
|
372
636
|
async function createSession(sessionOpts = {}) {
|
|
@@ -385,8 +649,12 @@ export function createWtManager(opts = {}) {
|
|
|
385
649
|
throw new Error("command is required");
|
|
386
650
|
}
|
|
387
651
|
|
|
388
|
-
await createTab(tab);
|
|
652
|
+
const createResult = await createTab(tab);
|
|
653
|
+
if (createResult?.success === false) {
|
|
654
|
+
return createResult;
|
|
655
|
+
}
|
|
389
656
|
sendKeysFn(String(sessionOpts.pane), String(sessionOpts.command), true);
|
|
657
|
+
return createResult;
|
|
390
658
|
}
|
|
391
659
|
|
|
392
660
|
function getTabCount() {
|
|
@@ -395,13 +663,16 @@ export function createWtManager(opts = {}) {
|
|
|
395
663
|
}
|
|
396
664
|
|
|
397
665
|
return Object.freeze({
|
|
666
|
+
ensureWtProfile,
|
|
398
667
|
createTab,
|
|
668
|
+
renameTab,
|
|
399
669
|
closeTab,
|
|
400
670
|
listTabs,
|
|
401
671
|
closeStale,
|
|
402
672
|
createSession,
|
|
403
673
|
splitPane,
|
|
404
674
|
applySplitLayout,
|
|
675
|
+
getEnvironmentInfo,
|
|
405
676
|
getTabCount,
|
|
406
677
|
});
|
|
407
678
|
}
|