@dotobokuri/fleet-cli 1.6.0 → 1.7.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/dist/index.js CHANGED
@@ -17560,7 +17560,9 @@ function killProcess(child, forceTimeoutMs = 3e3) {
17560
17560
  try {
17561
17561
  execSync(`taskkill /PID ${child.pid} /T /F`, {
17562
17562
  stdio: "pipe",
17563
- timeout: 5e3
17563
+ timeout: 5e3,
17564
+ // 콘솔 없는 호스트에서 taskkill 호출 시 새 콘솔 창이 깜빡이는 것을 방지한다.
17565
+ windowsHide: true
17564
17566
  });
17565
17567
  } catch {
17566
17568
  child.kill("SIGKILL");
@@ -17663,7 +17665,12 @@ var init_BaseConnection = __esm({
17663
17665
  cwd: this.cwd,
17664
17666
  stdio: ["pipe", "pipe", "pipe"],
17665
17667
  env: this.env,
17666
- windowsVerbatimArguments: true
17668
+ windowsVerbatimArguments: true,
17669
+ // 콘솔이 없는 호스트(예: fleet-console 백엔드)에서 cmd.exe를 spawn하면
17670
+ // Windows가 새 콘솔 창을 할당해 깜빡인다. stdio는 모두 pipe라 가시 콘솔이
17671
+ // 불필요하므로 CREATE_NO_WINDOW로 창 생성을 억제한다. (fleet-cli처럼 콘솔이
17672
+ // 이미 있는 경우에도 무해하다.)
17673
+ windowsHide: true
17667
17674
  }) : spawn(this.command, this.args, {
17668
17675
  cwd: this.cwd,
17669
17676
  stdio: ["pipe", "pipe", "pipe"],
@@ -18423,7 +18430,9 @@ function resolveNpxPath(env) {
18423
18430
  encoding: "utf-8",
18424
18431
  stdio: "pipe",
18425
18432
  timeout: 5e3,
18426
- env
18433
+ env,
18434
+ // 콘솔 없는 호스트에서 `where npx` 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
18435
+ windowsHide: true
18427
18436
  }).trim();
18428
18437
  const candidates = result.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
18429
18438
  if (windows) {
@@ -33897,15 +33906,21 @@ function claudeHooks(options2) {
33897
33906
  if (!hookExec) {
33898
33907
  throw new Error("Fleet Claude session hook command is required");
33899
33908
  }
33900
- const captureSessionHookExec = options2.captureSessionHookExec;
33909
+ const userPromptSubmitExecs = [options2.captureSessionHookExec, options2.turnStartHookExec].filter((exec) => exec !== void 0);
33910
+ const stopExecs = [options2.turnEndHookExec].filter((exec) => exec !== void 0);
33901
33911
  return {
33902
33912
  hooks: {
33903
33913
  SessionStart: [{
33904
33914
  hooks: [claudeCommandHook(hookExec)]
33905
33915
  }],
33906
- ...captureSessionHookExec ? {
33916
+ ...userPromptSubmitExecs.length > 0 ? {
33907
33917
  UserPromptSubmit: [{
33908
- hooks: [claudeCommandHook(captureSessionHookExec)]
33918
+ hooks: userPromptSubmitExecs.map(claudeCommandHook)
33919
+ }]
33920
+ } : {},
33921
+ ...stopExecs.length > 0 ? {
33922
+ Stop: [{
33923
+ hooks: stopExecs.map(claudeCommandHook)
33909
33924
  }]
33910
33925
  } : {}
33911
33926
  }
@@ -34668,11 +34683,17 @@ async function injectAgentCliProfile(profile, options2) {
34668
34683
  dataDir: options2.dataDir,
34669
34684
  rootDir: options2.pluginRootDir,
34670
34685
  captureSessionHookExec: options2.captureSessionHookExec,
34686
+ turnStartHookExec: options2.turnStartHookExec,
34687
+ turnEndHookExec: options2.turnEndHookExec,
34671
34688
  hookExec: startupDefinitions.host === "claude" ? requireHookExec(options2.hookExec) : void 0,
34672
34689
  withMarketplaceLock: options2.withMarketplaceLock
34673
34690
  });
34674
34691
  const codexPluginKeys = plugin.codexRegistrations.map((registration) => `${registration.pluginName}@${registration.marketplaceName}`);
34675
- const codexProfile = profile.id === "codex" ? writeCodexFleetProfile(profile.env, doctrine, codexPluginKeys, options2.captureSessionHookExec, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
34692
+ const codexProfile = profile.id === "codex" ? writeCodexFleetProfile(profile.env, doctrine, codexPluginKeys, {
34693
+ captureSessionHookExec: options2.captureSessionHookExec,
34694
+ turnStartHookExec: options2.turnStartHookExec,
34695
+ turnEndHookExec: options2.turnEndHookExec
34696
+ }, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
34676
34697
  const launchWarnings = [];
34677
34698
  for (const registration of plugin.codexRegistrations) {
34678
34699
  const registrationWarning = await ensureCodexPluginRegistered(registration, {
@@ -34748,7 +34769,7 @@ function writeSystemPromptFile(cliId, systemPrompt, onCleanup) {
34748
34769
  chmodBestEffort2(filePath, SYSTEM_PROMPT_FILE_MODE);
34749
34770
  return filePath;
34750
34771
  }
34751
- function writeCodexFleetProfile(env, doctrine, pluginKeys, captureSessionHookExec, onCleanup) {
34772
+ function writeCodexFleetProfile(env, doctrine, pluginKeys, hookExecs, onCleanup) {
34752
34773
  const codexHome = env.CODEX_HOME ?? path92.join(env.HOME ?? os23.homedir(), ".codex");
34753
34774
  mkdirSync32(codexHome, { recursive: true });
34754
34775
  pruneStaleCodexFleetProfiles(codexHome);
@@ -34768,19 +34789,31 @@ function writeCodexFleetProfile(env, doctrine, pluginKeys, captureSessionHookExe
34768
34789
  "enabled = true",
34769
34790
  ""
34770
34791
  ]),
34771
- ...codexCaptureHookConfig(captureSessionHookExec)
34792
+ ...codexHooksConfig(hookExecs)
34772
34793
  ].join("\n"));
34773
34794
  chmodBestEffort2(profilePath, SYSTEM_PROMPT_FILE_MODE);
34774
34795
  return { profileName, profilePath };
34775
34796
  }
34776
- function codexCaptureHookConfig(captureSessionHookExec) {
34777
- if (captureSessionHookExec === void 0) return [];
34778
- const command3 = buildPosixShellCommand([captureSessionHookExec.command, ...captureSessionHookExec.args]);
34779
- return [
34780
- "[hooks]",
34781
- `UserPromptSubmit = [{ hooks = [{ type = "command", command = "${escapeTomlBasicString2(command3)}" }] }]`,
34782
- ""
34783
- ];
34797
+ function codexHooksConfig(hookExecs) {
34798
+ const userPromptSubmitExecs = [hookExecs.captureSessionHookExec, hookExecs.turnStartHookExec].filter((exec) => exec !== void 0);
34799
+ const stopExecs = [hookExecs.turnEndHookExec].filter((exec) => exec !== void 0);
34800
+ if (userPromptSubmitExecs.length === 0 && stopExecs.length === 0) return [];
34801
+ const lines = ["[hooks]"];
34802
+ if (userPromptSubmitExecs.length > 0) {
34803
+ lines.push(`UserPromptSubmit = ${codexHookHandlersInline(userPromptSubmitExecs)}`);
34804
+ }
34805
+ if (stopExecs.length > 0) {
34806
+ lines.push(`Stop = ${codexHookHandlersInline(stopExecs)}`);
34807
+ }
34808
+ lines.push("");
34809
+ return lines;
34810
+ }
34811
+ function codexHookHandlersInline(execs) {
34812
+ const handlers = execs.map((exec) => {
34813
+ const command3 = buildPosixShellCommand([exec.command, ...exec.args]);
34814
+ return `{ type = "command", command = "${escapeTomlBasicString2(command3)}" }`;
34815
+ }).join(", ");
34816
+ return `[{ hooks = [${handlers}] }]`;
34784
34817
  }
34785
34818
  function writeFileNoFollow2(filePath, content) {
34786
34819
  const flags = constants32.O_WRONLY | constants32.O_CREAT | constants32.O_TRUNC | constants32.O_NOFOLLOW;
@@ -39685,7 +39718,7 @@ import * as fs52 from "fs";
39685
39718
  import fs5__default, { readFileSync as readFileSync8, existsSync as existsSync8, mkdtempSync as mkdtempSync3, writeFileSync as writeFileSync6, mkdirSync as mkdirSync8, rmSync as rmSync5, chmodSync as chmodSync5, renameSync as renameSync5, readdirSync as readdirSync6, constants as constants8, openSync as openSync8, closeSync as closeSync8, lstatSync as lstatSync8, unlinkSync as unlinkSync5, realpathSync as realpathSync6, statSync as statSync6, symlinkSync as symlinkSync2, fstatSync as fstatSync7 } from "fs";
39686
39719
  import * as path93 from "path";
39687
39720
  import path93__default, { join as join10, resolve, relative, dirname as dirname6 } from "path";
39688
- import process5 from "process";
39721
+ import process6 from "process";
39689
39722
  import { fileURLToPath, pathToFileURL as pathToFileURL2 } from "url";
39690
39723
  import * as os222 from "os";
39691
39724
  import os22__default from "os";
@@ -40637,10 +40670,10 @@ function mergeDefs2(...defs) {
40637
40670
  function cloneDef2(schema) {
40638
40671
  return mergeDefs2(schema._zod.def);
40639
40672
  }
40640
- function getElementAtPath2(obj, path34) {
40641
- if (!path34)
40673
+ function getElementAtPath2(obj, path35) {
40674
+ if (!path35)
40642
40675
  return obj;
40643
- return path34.reduce((acc, key) => acc?.[key], obj);
40676
+ return path35.reduce((acc, key) => acc?.[key], obj);
40644
40677
  }
40645
40678
  function promiseAllObject2(promisesObj) {
40646
40679
  const keys = Object.keys(promisesObj);
@@ -41049,11 +41082,11 @@ function explicitlyAborted2(x, startIndex = 0) {
41049
41082
  }
41050
41083
  return false;
41051
41084
  }
41052
- function prefixIssues2(path34, issues) {
41085
+ function prefixIssues2(path35, issues) {
41053
41086
  return issues.map((iss) => {
41054
41087
  var _a32;
41055
41088
  (_a32 = iss).path ?? (_a32.path = []);
41056
- iss.path.unshift(path34);
41089
+ iss.path.unshift(path35);
41057
41090
  return iss;
41058
41091
  });
41059
41092
  }
@@ -41198,16 +41231,16 @@ function flattenError2(error512, mapper = (issue32) => issue32.message) {
41198
41231
  }
41199
41232
  function formatError3(error512, mapper = (issue32) => issue32.message) {
41200
41233
  const fieldErrors = { _errors: [] };
41201
- const processError = (error522, path34 = []) => {
41234
+ const processError = (error522, path35 = []) => {
41202
41235
  for (const issue32 of error522.issues) {
41203
41236
  if (issue32.code === "invalid_union" && issue32.errors.length) {
41204
- issue32.errors.map((issues) => processError({ issues }, [...path34, ...issue32.path]));
41237
+ issue32.errors.map((issues) => processError({ issues }, [...path35, ...issue32.path]));
41205
41238
  } else if (issue32.code === "invalid_key") {
41206
- processError({ issues: issue32.issues }, [...path34, ...issue32.path]);
41239
+ processError({ issues: issue32.issues }, [...path35, ...issue32.path]);
41207
41240
  } else if (issue32.code === "invalid_element") {
41208
- processError({ issues: issue32.issues }, [...path34, ...issue32.path]);
41241
+ processError({ issues: issue32.issues }, [...path35, ...issue32.path]);
41209
41242
  } else {
41210
- const fullpath = [...path34, ...issue32.path];
41243
+ const fullpath = [...path35, ...issue32.path];
41211
41244
  if (fullpath.length === 0) {
41212
41245
  fieldErrors._errors.push(mapper(issue32));
41213
41246
  } else {
@@ -41234,17 +41267,17 @@ function formatError3(error512, mapper = (issue32) => issue32.message) {
41234
41267
  }
41235
41268
  function treeifyError2(error512, mapper = (issue32) => issue32.message) {
41236
41269
  const result = { errors: [] };
41237
- const processError = (error522, path34 = []) => {
41270
+ const processError = (error522, path35 = []) => {
41238
41271
  var _a32, _b;
41239
41272
  for (const issue32 of error522.issues) {
41240
41273
  if (issue32.code === "invalid_union" && issue32.errors.length) {
41241
- issue32.errors.map((issues) => processError({ issues }, [...path34, ...issue32.path]));
41274
+ issue32.errors.map((issues) => processError({ issues }, [...path35, ...issue32.path]));
41242
41275
  } else if (issue32.code === "invalid_key") {
41243
- processError({ issues: issue32.issues }, [...path34, ...issue32.path]);
41276
+ processError({ issues: issue32.issues }, [...path35, ...issue32.path]);
41244
41277
  } else if (issue32.code === "invalid_element") {
41245
- processError({ issues: issue32.issues }, [...path34, ...issue32.path]);
41278
+ processError({ issues: issue32.issues }, [...path35, ...issue32.path]);
41246
41279
  } else {
41247
- const fullpath = [...path34, ...issue32.path];
41280
+ const fullpath = [...path35, ...issue32.path];
41248
41281
  if (fullpath.length === 0) {
41249
41282
  result.errors.push(mapper(issue32));
41250
41283
  continue;
@@ -41276,8 +41309,8 @@ function treeifyError2(error512, mapper = (issue32) => issue32.message) {
41276
41309
  }
41277
41310
  function toDotPath2(_path) {
41278
41311
  const segs = [];
41279
- const path34 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
41280
- for (const seg of path34) {
41312
+ const path35 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
41313
+ for (const seg of path35) {
41281
41314
  if (typeof seg === "number")
41282
41315
  segs.push(`[${seg}]`);
41283
41316
  else if (typeof seg === "symbol")
@@ -53798,13 +53831,13 @@ function resolveRef2(ref, ctx) {
53798
53831
  if (!ref.startsWith("#")) {
53799
53832
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
53800
53833
  }
53801
- const path34 = ref.slice(1).split("/").filter(Boolean);
53802
- if (path34.length === 0) {
53834
+ const path35 = ref.slice(1).split("/").filter(Boolean);
53835
+ if (path35.length === 0) {
53803
53836
  return ctx.rootSchema;
53804
53837
  }
53805
53838
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
53806
- if (path34[0] === defsKey) {
53807
- const key = path34[1];
53839
+ if (path35[0] === defsKey) {
53840
+ const key = path35[1];
53808
53841
  if (!key || !ctx.defs[key]) {
53809
53842
  throw new Error(`Reference not found: ${ref}`);
53810
53843
  }
@@ -56555,7 +56588,9 @@ function killProcess2(child, forceTimeoutMs = 3e3) {
56555
56588
  try {
56556
56589
  execSync3(`taskkill /PID ${child.pid} /T /F`, {
56557
56590
  stdio: "pipe",
56558
- timeout: 5e3
56591
+ timeout: 5e3,
56592
+ // 콘솔 없는 호스트에서 taskkill 호출 시 새 콘솔 창이 깜빡이는 것을 방지한다.
56593
+ windowsHide: true
56559
56594
  });
56560
56595
  } catch {
56561
56596
  child.kill("SIGKILL");
@@ -56627,7 +56662,12 @@ var BaseConnection2 = class extends EventEmitter6 {
56627
56662
  cwd: this.cwd,
56628
56663
  stdio: ["pipe", "pipe", "pipe"],
56629
56664
  env: this.env,
56630
- windowsVerbatimArguments: true
56665
+ windowsVerbatimArguments: true,
56666
+ // 콘솔이 없는 호스트(예: fleet-console 백엔드)에서 cmd.exe를 spawn하면
56667
+ // Windows가 새 콘솔 창을 할당해 깜빡인다. stdio는 모두 pipe라 가시 콘솔이
56668
+ // 불필요하므로 CREATE_NO_WINDOW로 창 생성을 억제한다. (fleet-cli처럼 콘솔이
56669
+ // 이미 있는 경우에도 무해하다.)
56670
+ windowsHide: true
56631
56671
  }) : spawn3(this.command, this.args, {
56632
56672
  cwd: this.cwd,
56633
56673
  stdio: ["pipe", "pipe", "pipe"],
@@ -57381,7 +57421,9 @@ function resolveNpxPath2(env) {
57381
57421
  encoding: "utf-8",
57382
57422
  stdio: "pipe",
57383
57423
  timeout: 5e3,
57384
- env
57424
+ env,
57425
+ // 콘솔 없는 호스트에서 `where npx` 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
57426
+ windowsHide: true
57385
57427
  }).trim();
57386
57428
  const candidates = result.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
57387
57429
  if (windows) {
@@ -71400,7 +71442,7 @@ function createSessionCaptureHookExec(deps) {
71400
71442
  deps.provider
71401
71443
  ] : [deps.entryPath, "hook", "capture-session", deps.provider];
71402
71444
  return {
71403
- command: deps.execPath ?? process5.execPath,
71445
+ command: deps.execPath ?? process6.execPath,
71404
71446
  args
71405
71447
  };
71406
71448
  }
@@ -71758,15 +71800,21 @@ function claudeHooks2(options2) {
71758
71800
  if (!hookExec) {
71759
71801
  throw new Error("Fleet Claude session hook command is required");
71760
71802
  }
71761
- const captureSessionHookExec = options2.captureSessionHookExec;
71803
+ const userPromptSubmitExecs = [options2.captureSessionHookExec, options2.turnStartHookExec].filter((exec) => exec !== void 0);
71804
+ const stopExecs = [options2.turnEndHookExec].filter((exec) => exec !== void 0);
71762
71805
  return {
71763
71806
  hooks: {
71764
71807
  SessionStart: [{
71765
71808
  hooks: [claudeCommandHook2(hookExec)]
71766
71809
  }],
71767
- ...captureSessionHookExec ? {
71810
+ ...userPromptSubmitExecs.length > 0 ? {
71768
71811
  UserPromptSubmit: [{
71769
- hooks: [claudeCommandHook2(captureSessionHookExec)]
71812
+ hooks: userPromptSubmitExecs.map(claudeCommandHook2)
71813
+ }]
71814
+ } : {},
71815
+ ...stopExecs.length > 0 ? {
71816
+ Stop: [{
71817
+ hooks: stopExecs.map(claudeCommandHook2)
71770
71818
  }]
71771
71819
  } : {}
71772
71820
  }
@@ -72529,11 +72577,17 @@ async function injectAgentCliProfile2(profile, options2) {
72529
72577
  dataDir: options2.dataDir,
72530
72578
  rootDir: options2.pluginRootDir,
72531
72579
  captureSessionHookExec: options2.captureSessionHookExec,
72580
+ turnStartHookExec: options2.turnStartHookExec,
72581
+ turnEndHookExec: options2.turnEndHookExec,
72532
72582
  hookExec: startupDefinitions.host === "claude" ? requireHookExec2(options2.hookExec) : void 0,
72533
72583
  withMarketplaceLock: options2.withMarketplaceLock
72534
72584
  });
72535
72585
  const codexPluginKeys = plugin.codexRegistrations.map((registration) => `${registration.pluginName}@${registration.marketplaceName}`);
72536
- const codexProfile = profile.id === "codex" ? writeCodexFleetProfile2(profile.env, doctrine, codexPluginKeys, options2.captureSessionHookExec, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
72586
+ const codexProfile = profile.id === "codex" ? writeCodexFleetProfile2(profile.env, doctrine, codexPluginKeys, {
72587
+ captureSessionHookExec: options2.captureSessionHookExec,
72588
+ turnStartHookExec: options2.turnStartHookExec,
72589
+ turnEndHookExec: options2.turnEndHookExec
72590
+ }, (cleanup2) => tempCleanups.push(cleanup2)) : void 0;
72537
72591
  const launchWarnings = [];
72538
72592
  for (const registration of plugin.codexRegistrations) {
72539
72593
  const registrationWarning = await ensureCodexPluginRegistered2(registration, {
@@ -72609,7 +72663,7 @@ function writeSystemPromptFile2(cliId, systemPrompt, onCleanup) {
72609
72663
  chmodBestEffort22(filePath, SYSTEM_PROMPT_FILE_MODE2);
72610
72664
  return filePath;
72611
72665
  }
72612
- function writeCodexFleetProfile2(env, doctrine, pluginKeys, captureSessionHookExec, onCleanup) {
72666
+ function writeCodexFleetProfile2(env, doctrine, pluginKeys, hookExecs, onCleanup) {
72613
72667
  const codexHome = env.CODEX_HOME ?? path93__default.join(env.HOME ?? os22__default.homedir(), ".codex");
72614
72668
  mkdirSync8(codexHome, { recursive: true });
72615
72669
  pruneStaleCodexFleetProfiles2(codexHome);
@@ -72629,19 +72683,31 @@ function writeCodexFleetProfile2(env, doctrine, pluginKeys, captureSessionHookEx
72629
72683
  "enabled = true",
72630
72684
  ""
72631
72685
  ]),
72632
- ...codexCaptureHookConfig2(captureSessionHookExec)
72686
+ ...codexHooksConfig2(hookExecs)
72633
72687
  ].join("\n"));
72634
72688
  chmodBestEffort22(profilePath, SYSTEM_PROMPT_FILE_MODE2);
72635
72689
  return { profileName, profilePath };
72636
72690
  }
72637
- function codexCaptureHookConfig2(captureSessionHookExec) {
72638
- if (captureSessionHookExec === void 0) return [];
72639
- const command22 = buildPosixShellCommand2([captureSessionHookExec.command, ...captureSessionHookExec.args]);
72640
- return [
72641
- "[hooks]",
72642
- `UserPromptSubmit = [{ hooks = [{ type = "command", command = "${escapeTomlBasicString22(command22)}" }] }]`,
72643
- ""
72644
- ];
72691
+ function codexHooksConfig2(hookExecs) {
72692
+ const userPromptSubmitExecs = [hookExecs.captureSessionHookExec, hookExecs.turnStartHookExec].filter((exec) => exec !== void 0);
72693
+ const stopExecs = [hookExecs.turnEndHookExec].filter((exec) => exec !== void 0);
72694
+ if (userPromptSubmitExecs.length === 0 && stopExecs.length === 0) return [];
72695
+ const lines = ["[hooks]"];
72696
+ if (userPromptSubmitExecs.length > 0) {
72697
+ lines.push(`UserPromptSubmit = ${codexHookHandlersInline2(userPromptSubmitExecs)}`);
72698
+ }
72699
+ if (stopExecs.length > 0) {
72700
+ lines.push(`Stop = ${codexHookHandlersInline2(stopExecs)}`);
72701
+ }
72702
+ lines.push("");
72703
+ return lines;
72704
+ }
72705
+ function codexHookHandlersInline2(execs) {
72706
+ const handlers = execs.map((exec) => {
72707
+ const command22 = buildPosixShellCommand2([exec.command, ...exec.args]);
72708
+ return `{ type = "command", command = "${escapeTomlBasicString22(command22)}" }`;
72709
+ }).join(", ");
72710
+ return `[{ hooks = [${handlers}] }]`;
72645
72711
  }
72646
72712
  function writeFileNoFollow22(filePath, content) {
72647
72713
  const flags = constants8.O_WRONLY | constants8.O_CREAT | constants8.O_TRUNC | constants8.O_NOFOLLOW;
@@ -80781,6 +80847,12 @@ function createConsoleObservabilityStore(deps = {}) {
80781
80847
  session.status = status;
80782
80848
  return toTerminalSessionInfo(session);
80783
80849
  }
80850
+ function setTerminalSessionTurnState(sessionId, turnState) {
80851
+ const session = terminalSessionsById.get(sessionId);
80852
+ if (!session) return null;
80853
+ session.turnState = turnState;
80854
+ return toTerminalSessionInfo(session);
80855
+ }
80784
80856
  function transitionTerminalSessionToDormant(sessionId, providerSession) {
80785
80857
  const session = terminalSessionsById.get(sessionId);
80786
80858
  if (!session) return null;
@@ -80848,6 +80920,7 @@ function createConsoleObservabilityStore(deps = {}) {
80848
80920
  subscribeAll,
80849
80921
  updateTerminalSessionProviderSession,
80850
80922
  updateTerminalSessionStatus,
80923
+ setTerminalSessionTurnState,
80851
80924
  transitionTerminalSessionToDormant,
80852
80925
  removeTerminalSession,
80853
80926
  registerTerminalRuntimeSession,
@@ -80916,6 +80989,7 @@ function toTerminalSessionInfo(state) {
80916
80989
  cliId: state.cliId,
80917
80990
  cliLabel: state.cliLabel,
80918
80991
  status: state.status,
80992
+ turnState: state.turnState ?? "none",
80919
80993
  createdAt: state.createdAt,
80920
80994
  theaterId: state.theaterId,
80921
80995
  registrationId: state.registrationId,
@@ -81132,6 +81206,126 @@ function resolveConsolePath(pathname) {
81132
81206
  if (!path93__default.extname(withoutPrefix)) return "index.html";
81133
81207
  return withoutPrefix;
81134
81208
  }
81209
+ var TerminalFolderListError = class extends Error {
81210
+ code;
81211
+ constructor(code) {
81212
+ super(code);
81213
+ this.name = "TerminalFolderListError";
81214
+ this.code = code;
81215
+ }
81216
+ };
81217
+ var DIRECTORY_ENTRY_CAP = 500;
81218
+ var WINDOWS_DRIVE_LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");
81219
+ async function listTerminalFolders(requestedPath, deps = {}) {
81220
+ const platform2 = deps.platform ?? process.platform;
81221
+ const stat5 = deps.stat ?? fs5__default.promises.stat;
81222
+ const opendir = deps.opendir ?? fs5__default.promises.opendir;
81223
+ const targetPath = normalizeListPath(requestedPath, platform2, deps);
81224
+ const roots = await listRoots(platform2, stat5);
81225
+ const targetStat = await statDirectory(targetPath, stat5);
81226
+ if (!targetStat.isDirectory()) throw new TerminalFolderListError("invalid_path");
81227
+ const entries = [];
81228
+ const truncated = await collectDirectoryEntries(targetPath, opendir, stat5, entries);
81229
+ entries.sort((a, b) => a.name.localeCompare(b.name));
81230
+ return {
81231
+ path: targetPath,
81232
+ parentPath: parentPath(targetPath, platform2),
81233
+ roots,
81234
+ entries,
81235
+ ...truncated ? { truncated: true } : {}
81236
+ };
81237
+ }
81238
+ function normalizeFolderBrowserPath(value, platform2 = process.platform) {
81239
+ if (typeof value !== "string" || value.length === 0 || value.includes("\0")) throw new TerminalFolderListError("invalid_path");
81240
+ if (isWindowsAmbiguousPath(value, platform2) || !path93__default.isAbsolute(value)) throw new TerminalFolderListError("invalid_path");
81241
+ return path93__default.resolve(value);
81242
+ }
81243
+ function normalizeListPath(requestedPath, platform2, deps) {
81244
+ if (requestedPath === null || requestedPath === void 0) {
81245
+ const home = deps.homedir?.() ?? os22__default.homedir();
81246
+ const start = home || deps.cwd?.() || process.cwd();
81247
+ return path93__default.resolve(start);
81248
+ }
81249
+ return normalizeFolderBrowserPath(requestedPath, platform2);
81250
+ }
81251
+ async function listRoots(platform2, stat5) {
81252
+ if (platform2 !== "win32") return ["/"];
81253
+ const roots = [];
81254
+ await Promise.all(WINDOWS_DRIVE_LETTERS.map(async (letter) => {
81255
+ const root = `${letter}:\\`;
81256
+ try {
81257
+ if ((await stat5(root)).isDirectory()) roots.push(root);
81258
+ } catch {
81259
+ }
81260
+ }));
81261
+ return roots.sort();
81262
+ }
81263
+ async function statDirectory(targetPath, stat5) {
81264
+ try {
81265
+ return await stat5(targetPath);
81266
+ } catch (error512) {
81267
+ throw mapFsError(error512);
81268
+ }
81269
+ }
81270
+ async function collectDirectoryEntries(targetPath, opendir, stat5, entries) {
81271
+ const directory = await openDirectory(targetPath, opendir);
81272
+ try {
81273
+ let dirent = await directory.read();
81274
+ while (dirent !== null) {
81275
+ const entry = await toTerminalFolderEntry(targetPath, dirent, stat5);
81276
+ if (entry !== null) {
81277
+ if (entries.length >= DIRECTORY_ENTRY_CAP) return true;
81278
+ entries.push(entry);
81279
+ }
81280
+ dirent = await directory.read();
81281
+ }
81282
+ return false;
81283
+ } catch (error512) {
81284
+ throw mapFsError(error512);
81285
+ } finally {
81286
+ await directory.close();
81287
+ }
81288
+ }
81289
+ async function openDirectory(targetPath, opendir) {
81290
+ try {
81291
+ return await opendir(targetPath);
81292
+ } catch (error512) {
81293
+ throw mapFsError(error512);
81294
+ }
81295
+ }
81296
+ async function toTerminalFolderEntry(targetPath, dirent, stat5) {
81297
+ if (!dirent.isDirectory() && !dirent.isSymbolicLink()) return null;
81298
+ const entryPath = path93__default.join(targetPath, dirent.name);
81299
+ if (dirent.isDirectory()) return { name: dirent.name, path: entryPath, kind: "dir", accessible: true };
81300
+ const accessible = await statSymlinkDirectory(entryPath, stat5);
81301
+ if (accessible === null) return null;
81302
+ return { name: dirent.name, path: entryPath, kind: "dir", accessible };
81303
+ }
81304
+ async function statSymlinkDirectory(entryPath, stat5) {
81305
+ try {
81306
+ return (await stat5(entryPath)).isDirectory() ? true : null;
81307
+ } catch {
81308
+ return false;
81309
+ }
81310
+ }
81311
+ function mapFsError(error512) {
81312
+ const code = error512.code;
81313
+ if (code === "EACCES" || code === "EPERM") return new TerminalFolderListError("forbidden");
81314
+ if (code === "ENOENT" || code === "ENOTDIR") return new TerminalFolderListError("not_found");
81315
+ return new TerminalFolderListError("invalid_path");
81316
+ }
81317
+ function parentPath(targetPath, platform2) {
81318
+ const parsed = path93__default.parse(targetPath);
81319
+ const resolved = path93__default.resolve(targetPath);
81320
+ if (resolved === path93__default.resolve(parsed.root)) return null;
81321
+ const parent = path93__default.dirname(resolved);
81322
+ if (platform2 === "win32" && parent === resolved) return null;
81323
+ return parent;
81324
+ }
81325
+ function isWindowsAmbiguousPath(value, platform2) {
81326
+ if (platform2 !== "win32") return false;
81327
+ return /^[a-zA-Z]:(?![\\/])/.test(value) || /^[\\/](?![\\/])/.test(value);
81328
+ }
81135
81329
  var GRANT_ID_BYTES = 24;
81136
81330
  var DEFAULT_GRANT_TTL_MS = 6e4;
81137
81331
  function createFolderGrantStore(deps = {}) {
@@ -81163,165 +81357,15 @@ function createFolderGrantStore(deps = {}) {
81163
81357
  return { issue: issue32, consume };
81164
81358
  }
81165
81359
  function validateAbsoluteDirectory(cwd, statSync52 = fs5__default.statSync) {
81166
- if (!path93__default.isAbsolute(cwd)) throw new Error("invalid_folder");
81360
+ if (typeof cwd !== "string" || cwd.length === 0 || cwd.includes("\0")) throw new Error("invalid_folder");
81361
+ if (isWindowsAmbiguousPath2(cwd) || !path93__default.isAbsolute(cwd)) throw new Error("invalid_folder");
81167
81362
  const normalized = path93__default.resolve(cwd);
81168
81363
  if (!statSync52(normalized).isDirectory()) throw new Error("invalid_folder");
81169
81364
  return normalized;
81170
81365
  }
81171
- var execFileAsync = promisify(execFile2);
81172
- var DIALOG_TIMEOUT_MS = 3e4;
81173
- var POWERSHELL_FOLDER_PICKER_CSHARP = `
81174
- using System;
81175
- using System.Runtime.InteropServices;
81176
- namespace FleetPicker {
81177
- [ComImport, Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
81178
- public interface IShellItem {
81179
- void BindToHandler();
81180
- void GetParent();
81181
- [PreserveSig] int GetDisplayName(int sigdn, out IntPtr ppszName);
81182
- void GetAttributes();
81183
- void Compare();
81184
- }
81185
- [ComImport, Guid("d57c7288-d4ad-4768-be02-9d969532d960"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
81186
- public interface IFileOpenDialog {
81187
- [PreserveSig] int Show(IntPtr parent);
81188
- void SetFileTypes(); void SetFileTypeIndex(); void GetFileTypeIndex();
81189
- void Advise(); void Unadvise();
81190
- [PreserveSig] int SetOptions(uint fos);
81191
- [PreserveSig] int GetOptions(out uint pfos);
81192
- void SetDefaultFolder();
81193
- void SetFolder(IShellItem psi);
81194
- void GetFolder(); void GetCurrentSelection();
81195
- void SetFileName(); void GetFileName(); void SetTitle(); void SetOkButtonLabel(); void SetFileNameLabel();
81196
- [PreserveSig] int GetResult(out IShellItem ppsi);
81197
- void AddPlace(IShellItem psi, int fdap);
81198
- void SetDefaultExtension(); void Close(); void SetClientGuid(); void ClearClientData(); void SetFilter();
81199
- void GetResults(); void GetSelectedItems();
81200
- }
81201
- [ComImport, Guid("DC1C5A9C-E88A-4DDE-A5A1-60F82A20AEF7")] public class FileOpenDialog { }
81202
- public static class Picker {
81203
- [DllImport("shell32.dll", CharSet=CharSet.Unicode, PreserveSig=false)]
81204
- static extern void SHCreateItemFromParsingName(string pszPath, IntPtr pbc, ref Guid riid, [MarshalAs(UnmanagedType.Interface)] out IShellItem ppv);
81205
- static IShellItem ItemFromPath(string path) {
81206
- Guid iid = new Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe");
81207
- IShellItem it; SHCreateItemFromParsingName(path, IntPtr.Zero, ref iid, out it); return it;
81208
- }
81209
- public static string Pick(string initialPath) {
81210
- var dlg = (IFileOpenDialog)(new FileOpenDialog());
81211
- uint opts; dlg.GetOptions(out opts);
81212
- dlg.SetOptions(opts | 0x20); // FOS_PICKFOLDERS
81213
- if (!string.IsNullOrEmpty(initialPath)) {
81214
- // \uC2DC\uC791 \uD3F4\uB354\uB97C WSL \uC704\uCE58\uB85C \uC9C0\uC815\uD558\uACE0 \uC88C\uCE21\uC5D0 \uACE0\uC815(FDAP_TOP). \uC2E4\uD328\uD574\uB3C4 \uAE30\uBCF8 \uC704\uCE58\uB85C \uC9C4\uD589\uD55C\uB2E4.
81215
- try { IShellItem s = ItemFromPath(initialPath); dlg.AddPlace(s, 1); dlg.SetFolder(s); } catch { }
81216
- }
81217
- if (dlg.Show(IntPtr.Zero) != 0) return null; // \uCDE8\uC18C/\uC2E4\uD328 \uC2DC null
81218
- IShellItem item; dlg.GetResult(out item);
81219
- IntPtr ptr; if (item.GetDisplayName(unchecked((int)0x80058000), out ptr) != 0) return null; // SIGDN_FILESYSPATH; \uBE44 \uD30C\uC77C\uC2DC\uC2A4\uD15C \uC120\uD0DD \uAC00\uB4DC
81220
- string path = Marshal.PtrToStringAuto(ptr);
81221
- Marshal.FreeCoTaskMem(ptr);
81222
- return path;
81223
- }
81224
- }
81225
- }`;
81226
- function createNativeFolderPicker(deps = {}) {
81227
- const platform2 = deps.platform ?? process.platform;
81228
- const runCommand = deps.runCommand ?? runNativeCommand;
81229
- const statSync52 = deps.statSync ?? fs5__default.statSync;
81230
- const readProcVersion = deps.readProcVersion ?? defaultReadProcVersion;
81231
- const env = deps.env ?? process.env;
81232
- return async () => {
81233
- const isWsl = platform2 === "linux" && detectWsl(readProcVersion, env);
81234
- const wslStartPath = isWsl ? wslStartFolderUnc(env) : "";
81235
- const commands = buildPickerCommands(platform2, isWsl, wslStartPath);
81236
- if (commands.length === 0) return { kind: "error", error: "unsupported_platform" };
81237
- for (const command22 of commands) {
81238
- const result = await runPickerCommand(runCommand, command22);
81239
- if ("kind" in result && result.kind === "unavailable") continue;
81240
- if ("kind" in result && result.kind === "cancelled") return { kind: "cancelled" };
81241
- if ("kind" in result && result.kind === "timeout") return { kind: "error", error: "dialog_timeout" };
81242
- if ("kind" in result) continue;
81243
- let resolvedPath = result.stdout.trim();
81244
- if (command22.postProcess === "wslpath") {
81245
- try {
81246
- const wslResult = await runCommand("wslpath", ["-u", resolvedPath]);
81247
- resolvedPath = wslResult.stdout.trim();
81248
- } catch {
81249
- return { kind: "error", error: "invalid_folder" };
81250
- }
81251
- }
81252
- try {
81253
- return { kind: "selected", cwd: validateAbsoluteDirectory(resolvedPath, statSync52) };
81254
- } catch {
81255
- return { kind: "error", error: "invalid_folder" };
81256
- }
81257
- }
81258
- return { kind: "error", error: "dialog_unavailable" };
81259
- };
81260
- }
81261
- function defaultReadProcVersion() {
81262
- try {
81263
- return fs5__default.readFileSync("/proc/version", "utf8");
81264
- } catch {
81265
- return "";
81266
- }
81267
- }
81268
- function detectWsl(readProcVersion, env) {
81269
- if (env.WSL_INTEROP !== void 0 || env.WSL_DISTRO_NAME !== void 0) return true;
81270
- return /microsoft|wsl/i.test(readProcVersion());
81271
- }
81272
- function buildPickerCommands(platform2, isWsl = false, wslStartPath = "") {
81273
- if (platform2 === "darwin") {
81274
- return [{ bin: "osascript", args: ["-e", 'POSIX path of (choose folder with prompt "Choose a Fleet workspace")'] }];
81275
- }
81276
- if (platform2 === "linux") {
81277
- const commands = [];
81278
- if (isWsl) {
81279
- commands.push({ bin: "powershell.exe", args: buildPowerShellPickerArgs(wslStartPath), postProcess: "wslpath" });
81280
- }
81281
- commands.push(
81282
- { bin: "zenity", args: ["--file-selection", "--directory", "--title=Choose a Fleet workspace"] },
81283
- { bin: "kdialog", args: ["--getexistingdirectory", ".", "Choose a Fleet workspace"] }
81284
- );
81285
- return commands;
81286
- }
81287
- if (platform2 === "win32") {
81288
- return [{ bin: "powershell.exe", args: buildPowerShellPickerArgs("") }];
81289
- }
81290
- return [];
81291
- }
81292
- function buildPowerShellPickerArgs(initialWindowsPath) {
81293
- const escaped = initialWindowsPath.replace(/'/g, "''");
81294
- const script = [
81295
- "[Console]::OutputEncoding=[Text.Encoding]::UTF8",
81296
- `Add-Type -TypeDefinition '${POWERSHELL_FOLDER_PICKER_CSHARP}'`,
81297
- `$selection = [FleetPicker.Picker]::Pick('${escaped}')`,
81298
- "if ([string]::IsNullOrEmpty($selection)) { exit 1 }",
81299
- "[Console]::Out.Write($selection)"
81300
- ].join("\n");
81301
- return ["-NoProfile", "-Sta", "-Command", script];
81302
- }
81303
- function wslStartFolderUnc(env) {
81304
- const distro = env.WSL_DISTRO_NAME;
81305
- if (!distro) return "";
81306
- const home = env.HOME;
81307
- if (home && home.startsWith("/") && !home.startsWith("/mnt/")) {
81308
- return `\\\\wsl.localhost\\${distro}${home.replace(/\//g, "\\")}`;
81309
- }
81310
- return `\\\\wsl.localhost\\${distro}`;
81311
- }
81312
- async function runPickerCommand(runCommand, command22) {
81313
- try {
81314
- return await runCommand(command22.bin, command22.args);
81315
- } catch (error512) {
81316
- const err = error512;
81317
- if (err.code === "ENOENT") return { kind: "unavailable" };
81318
- if (err.code === "ETIMEDOUT") return { kind: "timeout" };
81319
- return { kind: "cancelled" };
81320
- }
81321
- }
81322
- async function runNativeCommand(bin, args) {
81323
- const result = await execFileAsync(bin, [...args], { timeout: DIALOG_TIMEOUT_MS, windowsHide: true });
81324
- return { stdout: result.stdout, stderr: result.stderr };
81366
+ function isWindowsAmbiguousPath2(value) {
81367
+ if (process.platform !== "win32") return false;
81368
+ return /^[a-zA-Z]:(?![\\/])/.test(value) || /^[\\/](?![\\/])/.test(value);
81325
81369
  }
81326
81370
  var JAVASCRIPT_ENTRY_EXTENSIONS2 = /* @__PURE__ */ new Set([".cjs", ".js", ".mjs"]);
81327
81371
  var TYPESCRIPT_ENTRY_EXTENSIONS2 = /* @__PURE__ */ new Set([".cts", ".mts", ".ts", ".tsx"]);
@@ -81330,23 +81374,10 @@ function buildConsoleHookCommand(entry) {
81330
81374
  if (entry === void 0) {
81331
81375
  throw new Error("Fleet Console session hook command requires the current console entry path");
81332
81376
  }
81333
- const extension = path93__default.extname(entry.entryPath);
81334
- if (JAVASCRIPT_ENTRY_EXTENSIONS2.has(extension)) {
81335
- return {
81336
- command: entry.execPath,
81337
- args: [entry.entryPath, "hook", "subagents-context"]
81338
- };
81339
- }
81340
- if (TYPESCRIPT_ENTRY_EXTENSIONS2.has(extension)) {
81341
- if (!entry.tsxLoaderPath) {
81342
- throw new Error("Fleet Console session hook command for TypeScript entries requires a tsx loader path");
81343
- }
81344
- return {
81345
- command: entry.execPath,
81346
- args: ["--import", pathToFileURL2(entry.tsxLoaderPath).href, entry.entryPath, "hook", "subagents-context"]
81347
- };
81348
- }
81349
- throw new Error(`Unsupported Fleet Console session hook entry extension: ${extension}`);
81377
+ return buildConsoleCliHookExec(entry, ["hook", "subagents-context"]);
81378
+ }
81379
+ function buildConsoleTurnHookCommand(entry, phase) {
81380
+ return buildConsoleCliHookExec(entry, ["hook", phase === "start" ? "turn-start" : "turn-end"]);
81350
81381
  }
81351
81382
  function buildConsoleCaptureHookCommand(entry, cliId) {
81352
81383
  const extension = path93__default.extname(entry.entryPath);
@@ -81377,7 +81408,9 @@ function runCodexCommand2(command22) {
81377
81408
  const result = spawnSync2(command22.bin, command22.args, {
81378
81409
  cwd: command22.cwd,
81379
81410
  encoding: "utf8",
81380
- env: command22.env
81411
+ env: command22.env,
81412
+ // 콘솔 없는 fleet-console 백엔드에서 codex 등록 명령 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
81413
+ windowsHide: true
81381
81414
  });
81382
81415
  return {
81383
81416
  status: result.status,
@@ -81390,18 +81423,37 @@ function withConsoleMarketplaceLock(target, fn) {
81390
81423
  mkdirSync8(path93__default.dirname(lockDir), { recursive: true });
81391
81424
  return withDirectoryLock2({ lockDir }, fn);
81392
81425
  }
81426
+ function buildConsoleCliHookExec(entry, trailingArgs) {
81427
+ const extension = path93__default.extname(entry.entryPath);
81428
+ if (JAVASCRIPT_ENTRY_EXTENSIONS2.has(extension)) {
81429
+ return {
81430
+ command: entry.execPath,
81431
+ args: [entry.entryPath, ...trailingArgs]
81432
+ };
81433
+ }
81434
+ if (TYPESCRIPT_ENTRY_EXTENSIONS2.has(extension)) {
81435
+ if (!entry.tsxLoaderPath) {
81436
+ throw new Error("Fleet Console session hook command for TypeScript entries requires a tsx loader path");
81437
+ }
81438
+ return {
81439
+ command: entry.execPath,
81440
+ args: ["--import", pathToFileURL2(entry.tsxLoaderPath).href, entry.entryPath, ...trailingArgs]
81441
+ };
81442
+ }
81443
+ throw new Error(`Unsupported Fleet Console session hook entry extension: ${extension}`);
81444
+ }
81393
81445
  var DEFAULT_TERMINAL_CWD_FALLBACK = os22__default.homedir;
81394
81446
  var TERMINAL_TERM = "xterm-256color";
81395
81447
  var CONSOLE_ENTRY_PATH = fileURLToPath(import.meta.url);
81396
81448
  var HOOK_ENTRY_EXTENSIONS = /* @__PURE__ */ new Set([".cjs", ".cts", ".js", ".mjs", ".mts", ".ts", ".tsx"]);
81397
81449
  var require2 = createRequire(import.meta.url);
81398
81450
  function createDefaultTerminalLaunchResolver(deps = {}) {
81399
- const baseCwd = deps.cwd ?? process5.cwd();
81400
- const env = deps.env ?? process5.env;
81401
- const execPath = deps.execPath ?? process5.execPath;
81451
+ const baseCwd = deps.cwd ?? process6.cwd();
81452
+ const env = deps.env ?? process6.env;
81453
+ const execPath = deps.execPath ?? process6.execPath;
81402
81454
  const homedir32 = deps.homedir ?? DEFAULT_TERMINAL_CWD_FALLBACK;
81403
- const platform2 = deps.platform ?? process5.platform;
81404
- const entryPath = resolveHookEntryPath(deps.entryPath ?? process5.argv[1]);
81455
+ const platform2 = deps.platform ?? process6.platform;
81456
+ const entryPath = resolveHookEntryPath(deps.entryPath ?? process6.argv[1]);
81405
81457
  const tsxLoaderPath = deps.tsxLoaderPath ?? resolveOptionalPackage("tsx");
81406
81458
  const dataDir = deps.dataDir ?? getFleetDataDir2();
81407
81459
  const infraServices = deps.infraServices ?? createInfraServices2();
@@ -81462,7 +81514,7 @@ function hasHookEntryExtension(entryPath) {
81462
81514
  }
81463
81515
  function startTerminalShell(launch, size) {
81464
81516
  const { spawn: spawnPty } = require2("node-pty");
81465
- const useConptyDll = resolveUseConptyDll2(process5.platform, process5.env);
81517
+ const useConptyDll = resolveUseConptyDll2(process6.platform, process6.env);
81466
81518
  return spawnPty(launch.bin, [...launch.args], {
81467
81519
  cols: size.cols,
81468
81520
  rows: size.rows,
@@ -81498,6 +81550,8 @@ async function createAgentCliLaunchSpec(options2) {
81498
81550
  dedicatedMcpSession: agentRuntime.dedicatedMcpSession,
81499
81551
  enableMetaphor: false,
81500
81552
  captureSessionHookExec: buildConsoleCaptureHookCommand(options2.hookEntry, profile.id),
81553
+ turnStartHookExec: buildConsoleTurnHookCommand(options2.hookEntry, "start"),
81554
+ turnEndHookExec: buildConsoleTurnHookCommand(options2.hookEntry, "end"),
81501
81555
  hookExec: buildConsoleHookCommand(options2.hookEntry),
81502
81556
  onCleanup: (cleanup) => cleanupStack.push(cleanup),
81503
81557
  replaceSystemPrompt: false,
@@ -81596,6 +81650,8 @@ var DEFAULT_COLS = 80;
81596
81650
  var DEFAULT_ROWS2 = 24;
81597
81651
  var DEFAULT_SCROLLBACK_LIMIT = 512;
81598
81652
  var WS_OPEN_STATE = 1;
81653
+ var THEATER_SHELL_SESSION_PREFIX = "shell:";
81654
+ var THEATER_SHELL_DETACH_GRACE_MS = 4e3;
81599
81655
  function createTerminalSessionManager(deps) {
81600
81656
  const startShell2 = deps.startShell ?? startTerminalShell;
81601
81657
  const scrollbackLimit = deps.scrollbackLimit ?? DEFAULT_SCROLLBACK_LIMIT;
@@ -81606,6 +81662,7 @@ function createTerminalSessionManager(deps) {
81606
81662
  }
81607
81663
  async function attach(socket, context) {
81608
81664
  const session = await getOrCreateSession(context);
81665
+ clearGraceTimer(session);
81609
81666
  if (session.activeSocket && session.activeSocket !== socket) {
81610
81667
  session.activeSocket.close(4e3, "terminal_replaced");
81611
81668
  }
@@ -81685,7 +81742,8 @@ function createTerminalSessionManager(deps) {
81685
81742
  renameCommand: launch.renameCommand,
81686
81743
  activeSocket: null,
81687
81744
  cols: DEFAULT_COLS,
81688
- rows: DEFAULT_ROWS2
81745
+ rows: DEFAULT_ROWS2,
81746
+ graceTimer: null
81689
81747
  };
81690
81748
  const dataDisposable = pty.onData((data) => handlePtyData(session, data));
81691
81749
  const exitDisposable = pty.onExit(() => removeSession(session));
@@ -81723,6 +81781,13 @@ function createTerminalSessionManager(deps) {
81723
81781
  function detachSocket(session, socket) {
81724
81782
  if (session.activeSocket !== socket) return;
81725
81783
  session.activeSocket = null;
81784
+ if (isTheaterShell(session.id)) {
81785
+ clearGraceTimer(session);
81786
+ session.graceTimer = setTimeout(() => {
81787
+ session.graceTimer = null;
81788
+ if (session.activeSocket === null) removeSession(session);
81789
+ }, THEATER_SHELL_DETACH_GRACE_MS);
81790
+ }
81726
81791
  }
81727
81792
  function replayScrollback(session, socket) {
81728
81793
  for (const chunk of session.scrollback) {
@@ -81738,6 +81803,7 @@ function createTerminalSessionManager(deps) {
81738
81803
  }
81739
81804
  async function killSession(session, options2 = {}) {
81740
81805
  const killPty = options2.killPty ?? true;
81806
+ clearGraceTimer(session);
81741
81807
  session.activeSocket?.close(4001, "terminal_closed");
81742
81808
  session.activeSocket = null;
81743
81809
  for (const disposable of session.disposables) disposable.dispose();
@@ -81761,6 +81827,14 @@ function killPtyBestEffort2(pty) {
81761
81827
  } catch {
81762
81828
  }
81763
81829
  }
81830
+ function isTheaterShell(sessionId) {
81831
+ return sessionId.startsWith(THEATER_SHELL_SESSION_PREFIX);
81832
+ }
81833
+ function clearGraceTimer(session) {
81834
+ if (session.graceTimer === null) return;
81835
+ clearTimeout(session.graceTimer);
81836
+ session.graceTimer = null;
81837
+ }
81764
81838
  function toBuffer(data) {
81765
81839
  if (Buffer.isBuffer(data)) return data;
81766
81840
  if (Array.isArray(data)) return Buffer.concat(data);
@@ -81804,7 +81878,7 @@ function createTerminalTicketRegistry(deps = {}) {
81804
81878
  }
81805
81879
  return { ttlMs, issue: issue32, consume, prune };
81806
81880
  }
81807
- var execFileAsync2 = promisify(execFile2);
81881
+ var execFileAsync = promisify(execFile2);
81808
81882
  var GIT_STATUS_TIMEOUT_MS = 4e3;
81809
81883
  var GIT_STATUS_MAX_BUFFER = 1024 * 1024;
81810
81884
  var GIT_HASH_TIMEOUT_MS = 4e3;
@@ -81813,11 +81887,13 @@ function createWorkspaceChangeScanner() {
81813
81887
  return {
81814
81888
  async snapshot(cwd) {
81815
81889
  try {
81816
- const { stdout } = await execFileAsync2("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
81890
+ const { stdout } = await execFileAsync("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
81817
81891
  cwd,
81818
81892
  encoding: "utf8",
81819
81893
  maxBuffer: GIT_STATUS_MAX_BUFFER,
81820
- timeout: GIT_STATUS_TIMEOUT_MS
81894
+ timeout: GIT_STATUS_TIMEOUT_MS,
81895
+ // 콘솔 없는 fleet-console 백엔드에서 git.exe 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
81896
+ windowsHide: true
81821
81897
  });
81822
81898
  const entries = parseGitStatusPorcelainZ(stdout);
81823
81899
  if (!entries) return null;
@@ -81877,7 +81953,9 @@ function execGitHashObject(cwd, paths) {
81877
81953
  cwd,
81878
81954
  encoding: "utf8",
81879
81955
  maxBuffer: GIT_HASH_MAX_BUFFER,
81880
- timeout: GIT_HASH_TIMEOUT_MS
81956
+ timeout: GIT_HASH_TIMEOUT_MS,
81957
+ // 콘솔 없는 fleet-console 백엔드에서 git.exe 실행 시 콘솔 창이 깜빡이는 것을 방지한다.
81958
+ windowsHide: true
81881
81959
  },
81882
81960
  (error512, stdout) => {
81883
81961
  if (error512) {
@@ -82054,6 +82132,7 @@ function createConsoleUpdateCheckService(deps = {}) {
82054
82132
  var DEFAULT_HOST22 = "127.0.0.1";
82055
82133
  var DEFAULT_PORT22 = 0;
82056
82134
  var SHELL_TERMINAL_SESSION_ID = "shell";
82135
+ var THEATER_SHELL_SESSION_PREFIX2 = "shell:";
82057
82136
  var SERVER_TIMEOUT_MS = 30 * 60 * 1e3;
82058
82137
  var MAX_BODY_BYTES = 1024 * 1024;
82059
82138
  function createConsoleServer(deps = {}) {
@@ -82098,7 +82177,6 @@ function createConsoleServer(deps = {}) {
82098
82177
  getPort: () => lockHandle?.payload.port ?? port,
82099
82178
  getAdminToken: () => lockHandle?.payload.token ?? null
82100
82179
  });
82101
- const pickTerminalFolder = deps.terminalPickFolder ?? createNativeFolderPicker();
82102
82180
  const pendingRuntimeSessions = /* @__PURE__ */ new Map();
82103
82181
  const terminalSessions = createTerminalSessionManager({
82104
82182
  launch: deps.terminalLaunch ?? createDefaultTerminalLaunchResolver({
@@ -82180,8 +82258,12 @@ function createConsoleServer(deps = {}) {
82180
82258
  runAsyncHandler(handleTerminalTicket(req, res), res);
82181
82259
  return;
82182
82260
  }
82183
- if (pathname === "/terminal/folders/pick") {
82184
- runAsyncHandler(handleTerminalFolderPick(req, res), res);
82261
+ if (pathname === "/terminal/folders/list") {
82262
+ runAsyncHandler(handleTerminalFoldersList(req, res), res);
82263
+ return;
82264
+ }
82265
+ if (pathname === "/terminal/folders/grants") {
82266
+ runAsyncHandler(handleTerminalFolderGrants(req, res), res);
82185
82267
  return;
82186
82268
  }
82187
82269
  if (pathname === "/terminal/sessions") {
@@ -82193,6 +82275,11 @@ function createConsoleServer(deps = {}) {
82193
82275
  runAsyncHandler(handleTerminalSessionResume(req, res, decodeURIComponent(terminalSessionResumeMatch[1] ?? "")), res);
82194
82276
  return;
82195
82277
  }
82278
+ const terminalSessionTurnMatch = pathname.match(/^\/terminal\/sessions\/([^/]+)\/turn$/);
82279
+ if (terminalSessionTurnMatch) {
82280
+ runAsyncHandler(handleTerminalSessionTurn(req, res, decodeURIComponent(terminalSessionTurnMatch[1] ?? "")), res);
82281
+ return;
82282
+ }
82196
82283
  const terminalSessionItemMatch = pathname.match(/^\/terminal\/sessions\/([^/]+)$/);
82197
82284
  if (terminalSessionItemMatch) {
82198
82285
  runAsyncHandler(handleTerminalSessionItem(req, res, decodeURIComponent(terminalSessionItemMatch[1] ?? "")), res);
@@ -82259,6 +82346,30 @@ function createConsoleServer(deps = {}) {
82259
82346
  };
82260
82347
  writeJson(res, 200, body);
82261
82348
  }
82349
+ async function handleTerminalSessionTurn(req, res, sessionId) {
82350
+ if (req.method !== "POST") {
82351
+ writeJson(res, 405, { error: "Method not allowed" });
82352
+ return;
82353
+ }
82354
+ const token = lockHandle?.payload.token;
82355
+ if (!token || req.headers.authorization !== `Bearer ${token}`) {
82356
+ writeJson(res, 401, { error: "Unauthorized" });
82357
+ return;
82358
+ }
82359
+ const body = await readJsonBody(req);
82360
+ const turnState = body?.phase === "start" ? "running" : body?.phase === "end" ? "ended" : null;
82361
+ if (turnState === null) {
82362
+ writeJson(res, 400, { error: "invalid_phase" });
82363
+ return;
82364
+ }
82365
+ const updated = observability.setTerminalSessionTurnState(sessionId, turnState);
82366
+ if (!updated) {
82367
+ writeJson(res, 404, { error: "terminal_session_not_found" });
82368
+ return;
82369
+ }
82370
+ observability.notifySessionUpdated(updated);
82371
+ writeJson(res, 200, { ok: true });
82372
+ }
82262
82373
  async function handleTerminalTicket(req, res) {
82263
82374
  if (req.method !== "POST") {
82264
82375
  writeJson(res, 405, { error: "Method not allowed" });
@@ -82271,16 +82382,29 @@ function createConsoleServer(deps = {}) {
82271
82382
  const body = await readJsonBody(req);
82272
82383
  const kind = body?.kind === "shell" ? "shell" : "fleet";
82273
82384
  const requestedSessionId = body?.sessionId;
82385
+ const requestedTheaterId = typeof body?.theaterId === "string" ? body.theaterId : void 0;
82274
82386
  let sessionId;
82387
+ let cwd;
82275
82388
  if (kind === "shell") {
82276
- sessionId = SHELL_TERMINAL_SESSION_ID;
82389
+ if (typeof requestedSessionId === "string" && requestedSessionId.startsWith(THEATER_SHELL_SESSION_PREFIX2) && requestedTheaterId) {
82390
+ const theater = theaters.get(requestedTheaterId);
82391
+ if (!theater) {
82392
+ writeJson(res, 404, { error: "theater_not_found" });
82393
+ return;
82394
+ }
82395
+ sessionId = requestedSessionId;
82396
+ cwd = theater.path;
82397
+ } else {
82398
+ sessionId = SHELL_TERMINAL_SESSION_ID;
82399
+ cwd = "";
82400
+ }
82277
82401
  } else if (typeof requestedSessionId === "string" && requestedSessionId.length > 0) {
82278
82402
  sessionId = requestedSessionId;
82403
+ cwd = observability.getLaunchCwd(sessionId);
82279
82404
  } else {
82280
82405
  writeJson(res, 400, { error: "terminal_session_not_found" });
82281
82406
  return;
82282
82407
  }
82283
- const cwd = kind === "shell" ? "" : observability.getLaunchCwd(sessionId);
82284
82408
  if (cwd === null) {
82285
82409
  writeJson(res, 404, { error: "terminal_session_not_found" });
82286
82410
  return;
@@ -82295,7 +82419,7 @@ function createConsoleServer(deps = {}) {
82295
82419
  kind
82296
82420
  }));
82297
82421
  }
82298
- async function handleTerminalFolderPick(req, res) {
82422
+ async function handleTerminalFoldersList(req, res) {
82299
82423
  if (req.method !== "POST") {
82300
82424
  writeJson(res, 405, { error: "Method not allowed" });
82301
82425
  return;
@@ -82304,16 +82428,41 @@ function createConsoleServer(deps = {}) {
82304
82428
  writeJson(res, 401, { error: "unauthorized" });
82305
82429
  return;
82306
82430
  }
82307
- const result = await pickTerminalFolder();
82308
- if (result.kind === "cancelled") {
82309
- writeJson(res, 200, { cancelled: true });
82431
+ const body = await readJsonBody(req);
82432
+ if (!isPlainObject22(body) || body.path !== void 0 && body.path !== null && typeof body.path !== "string") {
82433
+ writeJson(res, 400, { error: "invalid_path" });
82434
+ return;
82435
+ }
82436
+ try {
82437
+ const payload = await listTerminalFolders(body.path === void 0 ? null : body.path);
82438
+ writeJson(res, 200, payload);
82439
+ } catch (error512) {
82440
+ if (error512 instanceof TerminalFolderListError) {
82441
+ writeJson(res, terminalFolderListStatus(error512), { error: error512.code });
82442
+ return;
82443
+ }
82444
+ throw error512;
82445
+ }
82446
+ }
82447
+ async function handleTerminalFolderGrants(req, res) {
82448
+ if (req.method !== "POST") {
82449
+ writeJson(res, 405, { error: "Method not allowed" });
82450
+ return;
82451
+ }
82452
+ if (!isTerminalAuthorized(req)) {
82453
+ writeJson(res, 401, { error: "unauthorized" });
82310
82454
  return;
82311
82455
  }
82312
- if (result.kind === "error") {
82313
- writeJson(res, result.error === "invalid_folder" ? 400 : 503, { error: result.error });
82456
+ const body = await readJsonBody(req);
82457
+ if (!isPlainObject22(body) || typeof body.path !== "string") {
82458
+ writeJson(res, 400, { error: "invalid_folder" });
82314
82459
  return;
82315
82460
  }
82316
- writeJson(res, 200, { folderGrantId: folderGrants.issue(result.cwd) });
82461
+ try {
82462
+ writeJson(res, 200, { folderGrantId: folderGrants.issue(body.path) });
82463
+ } catch {
82464
+ writeJson(res, 400, { error: "invalid_folder" });
82465
+ }
82317
82466
  }
82318
82467
  async function handleTerminalSessions(req, res) {
82319
82468
  if (!isTerminalAuthorized(req)) {
@@ -82443,19 +82592,20 @@ function createConsoleServer(deps = {}) {
82443
82592
  writeJson(res, 401, { error: "unauthorized" });
82444
82593
  return;
82445
82594
  }
82446
- const result = await pickTerminalFolder();
82447
- if (result.kind === "cancelled") {
82448
- writeJson(res, 200, { cancelled: true });
82595
+ const body = await readJsonBody(req);
82596
+ if (!isPlainObject22(body) || typeof body.folderGrantId !== "string") {
82597
+ writeJson(res, 400, { error: "invalid_folder_grant" });
82449
82598
  return;
82450
82599
  }
82451
- if (result.kind === "error") {
82452
- writeJson(res, result.error === "invalid_folder" ? 400 : 503, { error: result.error });
82600
+ const cwd = folderGrants.consume(body.folderGrantId);
82601
+ if (!cwd) {
82602
+ writeJson(res, 400, { error: "invalid_folder_grant" });
82453
82603
  return;
82454
82604
  }
82455
- const theater = await theaters.register(result.cwd);
82605
+ const theater = await theaters.register(cwd);
82456
82606
  let hasWiki = false;
82457
82607
  try {
82458
- await codex.registerWorkspace(result.cwd);
82608
+ await codex.registerWorkspace(cwd);
82459
82609
  hasWiki = true;
82460
82610
  } catch (error512) {
82461
82611
  if (!(error512 instanceof Error && error512.message === "knowledge_root_missing")) {
@@ -82475,11 +82625,7 @@ function createConsoleServer(deps = {}) {
82475
82625
  return;
82476
82626
  }
82477
82627
  const operations = observability.listDurableOperations().filter((operation) => operation.theaterId === theaterId);
82478
- const removed = theaters.remove(theaterId);
82479
- if (!removed) {
82480
- writeJson(res, 404, { error: "theater_not_found" });
82481
- return;
82482
- }
82628
+ theaters.remove(theaterId);
82483
82629
  for (const operation of operations) forgetTerminalSession(operation.sessionId, { persist: false });
82484
82630
  persistDurableState();
82485
82631
  writeJson(res, 200, { ok: true });
@@ -82867,6 +83013,14 @@ function readOptionalAgentCliId(value, res) {
82867
83013
  return false;
82868
83014
  }
82869
83015
  }
83016
+ function terminalFolderListStatus(error512) {
83017
+ if (error512.code === "forbidden") return 403;
83018
+ if (error512.code === "not_found") return 404;
83019
+ return 400;
83020
+ }
83021
+ function isPlainObject22(value) {
83022
+ return typeof value === "object" && value !== null && !Array.isArray(value);
83023
+ }
82870
83024
  async function readJsonBody(req) {
82871
83025
  const chunks = [];
82872
83026
  let total = 0;
@@ -82926,9 +83080,9 @@ function isAllowedTerminalOrigin(req, expectedPort) {
82926
83080
  return origin === `http://127.0.0.1:${expectedPort}`;
82927
83081
  }
82928
83082
  var CAPTURE_TEMP_PREFIX = ".capture.";
82929
- async function runCaptureSessionHook(provider, env = process5.env) {
83083
+ async function runCaptureSessionHook(provider, env = process6.env) {
82930
83084
  return captureSession({
82931
- diagnostics: process5.stderr,
83085
+ diagnostics: process6.stderr,
82932
83086
  env,
82933
83087
  input: await readStdin(),
82934
83088
  provider
@@ -82945,12 +83099,12 @@ function captureSession(options2) {
82945
83099
  }
82946
83100
  function captureSessionStrict(options2) {
82947
83101
  const provider = parseProvider(options2.provider);
82948
- const fleetSessionId = readFleetSessionId(options2.env ?? process5.env);
83102
+ const fleetSessionId = readFleetSessionId(options2.env ?? process6.env);
82949
83103
  const input = parseHookInput(options2.input ?? "");
82950
83104
  const providerSession = toProviderSession(provider, input, options2.now ?? (() => /* @__PURE__ */ new Date()));
82951
83105
  const capturesDir = (options2.paths ?? createConsoleDataPaths()).capturesDir;
82952
83106
  const finalPath = path93__default.join(capturesDir, `${fleetSessionId}.json`);
82953
- const tempPath = path93__default.join(capturesDir, `${CAPTURE_TEMP_PREFIX}${fleetSessionId}.${process5.pid}.${Date.now()}.tmp`);
83107
+ const tempPath = path93__default.join(capturesDir, `${CAPTURE_TEMP_PREFIX}${fleetSessionId}.${process6.pid}.${Date.now()}.tmp`);
82954
83108
  fs5__default.mkdirSync(capturesDir, { recursive: true, mode: 448 });
82955
83109
  fs5__default.writeFileSync(tempPath, `${JSON.stringify(providerSession, null, 2)}
82956
83110
  `, { mode: 384 });
@@ -82990,11 +83144,11 @@ function toProviderSession(provider, input, now) {
82990
83144
  function readStdin() {
82991
83145
  return new Promise((resolve2, reject) => {
82992
83146
  const chunks = [];
82993
- process5.stdin.on("data", (chunk) => {
83147
+ process6.stdin.on("data", (chunk) => {
82994
83148
  chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
82995
83149
  });
82996
- process5.stdin.on("error", reject);
82997
- process5.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
83150
+ process6.stdin.on("error", reject);
83151
+ process6.stdin.on("end", () => resolve2(Buffer.concat(chunks).toString("utf8")));
82998
83152
  });
82999
83153
  }
83000
83154
  function createConsoleStalePolicy(deps = {}) {
@@ -83009,10 +83163,40 @@ function createConsoleStalePolicy(deps = {}) {
83009
83163
  }
83010
83164
  return { isBuildStale };
83011
83165
  }
83166
+ var TURN_POST_TIMEOUT_MS = 1500;
83167
+ async function runTurnStateHook(phase, env = process6.env, options2 = {}) {
83168
+ try {
83169
+ await postTurnState(phase, env, options2);
83170
+ } catch {
83171
+ }
83172
+ }
83173
+ async function postTurnState(phase, env, options2) {
83174
+ const sessionId = env.FLEET_CONSOLE_SESSION_ID;
83175
+ if (!sessionId) return;
83176
+ const paths = createConsolePaths({ env });
83177
+ const lock = createConsoleLock().readLock(paths.lockFile);
83178
+ if (!lock) return;
83179
+ const fetchImpl = options2.fetchImpl ?? fetch;
83180
+ const controller = new AbortController();
83181
+ const timer = setTimeout(() => controller.abort(), options2.timeoutMs ?? TURN_POST_TIMEOUT_MS);
83182
+ try {
83183
+ await fetchImpl(`${lock.endpoint}terminal/sessions/${encodeURIComponent(sessionId)}/turn`, {
83184
+ method: "POST",
83185
+ headers: {
83186
+ authorization: `Bearer ${lock.token}`,
83187
+ "content-type": "application/json"
83188
+ },
83189
+ body: JSON.stringify({ phase }),
83190
+ signal: controller.signal
83191
+ });
83192
+ } finally {
83193
+ clearTimeout(timer);
83194
+ }
83195
+ }
83012
83196
  var FIXED_HOST = "127.0.0.1";
83013
83197
  var HELP_BANNER_INDENT = " ";
83014
83198
  var DEFAULT_HELP_RELEASE = "local";
83015
- var CONSOLE_HOOK_COMMANDS = /* @__PURE__ */ new Set(["capture-session", "subagents-context"]);
83199
+ var CONSOLE_HOOK_COMMANDS = /* @__PURE__ */ new Set(["capture-session", "subagents-context", "turn-start", "turn-end"]);
83016
83200
  function parseConsoleCliMode(argv2) {
83017
83201
  if (argv2.length === 0) return "start";
83018
83202
  const [first, ...rest] = argv2;
@@ -83043,6 +83227,8 @@ function parseConsoleHookCommand(argv2) {
83043
83227
  throw new Error("Unknown fleet-console hook command");
83044
83228
  }
83045
83229
  if (commandName === "subagents-context" && rest.length === 0) return { command: "subagents-context" };
83230
+ if (commandName === "turn-start" && rest.length === 0) return { command: "turn-start" };
83231
+ if (commandName === "turn-end" && rest.length === 0) return { command: "turn-end" };
83046
83232
  if (commandName === "capture-session" && rest.length === 1 && rest[0]) return { command: "capture-session", provider: rest[0] };
83047
83233
  throw new Error("Unknown fleet-console hook command");
83048
83234
  }
@@ -83106,8 +83292,8 @@ function buildWikiHelpText(options2 = {}) {
83106
83292
  return colorEnabled ? text : stripAnsi2(text);
83107
83293
  }
83108
83294
  function createConsoleDaemonLifecycle(deps = {}) {
83109
- const env = deps.env ?? process5.env;
83110
- const execPath = deps.execPath ?? process5.execPath;
83295
+ const env = deps.env ?? process6.env;
83296
+ const execPath = deps.execPath ?? process6.execPath;
83111
83297
  const serverModulePath = deps.serverModulePath ?? resolveDefaultServerModulePath();
83112
83298
  const spawnDetached = deps.spawnDetached ?? ((bin, args, options2) => {
83113
83299
  spawn3(bin, [...args], options2).unref();
@@ -83121,10 +83307,10 @@ function createConsoleDaemonLifecycle(deps = {}) {
83121
83307
  const server = createConsoleServer();
83122
83308
  await server.start(paths);
83123
83309
  await new Promise((resolve2) => {
83124
- process5.once("SIGTERM", () => {
83310
+ process6.once("SIGTERM", () => {
83125
83311
  void server.stop().finally(resolve2);
83126
83312
  });
83127
- process5.once("SIGINT", () => {
83313
+ process6.once("SIGINT", () => {
83128
83314
  void server.stop().finally(resolve2);
83129
83315
  });
83130
83316
  });
@@ -83138,14 +83324,14 @@ function createConsoleDaemonLifecycle(deps = {}) {
83138
83324
  const payload = readTrustedLock();
83139
83325
  if (!payload) return;
83140
83326
  try {
83141
- process5.kill(payload.pid, "SIGTERM");
83327
+ process6.kill(payload.pid, "SIGTERM");
83142
83328
  } catch (err) {
83143
83329
  if (err.code !== "ESRCH") throw err;
83144
83330
  }
83145
83331
  await sleep(200);
83146
83332
  try {
83147
- process5.kill(payload.pid, 0);
83148
- process5.kill(payload.pid, "SIGKILL");
83333
+ process6.kill(payload.pid, 0);
83334
+ process6.kill(payload.pid, "SIGKILL");
83149
83335
  } catch (err) {
83150
83336
  if (err.code !== "ESRCH") throw err;
83151
83337
  }
@@ -83263,51 +83449,55 @@ async function runConsoleRestart(deps = {}) {
83263
83449
  return openFleetConsole({ lifecycle, openBrowser: deps.openBrowser });
83264
83450
  }
83265
83451
  async function main() {
83266
- if (process5.argv[2] === "serve") {
83452
+ if (process6.argv[2] === "serve") {
83267
83453
  await createConsoleDaemonLifecycle().runServer();
83268
83454
  return;
83269
83455
  }
83270
- if (process5.argv[2] === "hook") {
83271
- const hookCommand = parseConsoleHookCommand(process5.argv.slice(3));
83456
+ if (process6.argv[2] === "hook") {
83457
+ const hookCommand = parseConsoleHookCommand(process6.argv.slice(3));
83272
83458
  if (hookCommand.command === "subagents-context") {
83273
- process5.stdout.write(`${runSubagentsContextHook(process5.env)}
83459
+ process6.stdout.write(`${runSubagentsContextHook(process6.env)}
83274
83460
  `);
83275
83461
  return;
83276
83462
  }
83277
- await runCaptureSessionHook(hookCommand.provider, process5.env);
83463
+ if (hookCommand.command === "turn-start" || hookCommand.command === "turn-end") {
83464
+ await runTurnStateHook(hookCommand.command === "turn-start" ? "start" : "end", process6.env);
83465
+ return;
83466
+ }
83467
+ await runCaptureSessionHook(hookCommand.provider, process6.env);
83278
83468
  return;
83279
83469
  }
83280
- if (process5.argv[2] === "codex") {
83281
- await mainWiki(process5.argv.slice(3));
83470
+ if (process6.argv[2] === "codex") {
83471
+ await mainWiki(process6.argv.slice(3));
83282
83472
  return;
83283
83473
  }
83284
- const mode = parseConsoleCliMode(process5.argv.slice(2));
83474
+ const mode = parseConsoleCliMode(process6.argv.slice(2));
83285
83475
  if (mode === "help") {
83286
- process5.stdout.write(`${buildConsoleHelpText({ env: process5.env, isTTY: process5.stdout.isTTY })}
83476
+ process6.stdout.write(`${buildConsoleHelpText({ env: process6.env, isTTY: process6.stdout.isTTY })}
83287
83477
  `);
83288
83478
  return;
83289
83479
  }
83290
83480
  if (mode === "status") {
83291
- process5.stdout.write(`${await runConsoleStatus()}
83481
+ process6.stdout.write(`${await runConsoleStatus()}
83292
83482
  `);
83293
83483
  return;
83294
83484
  }
83295
83485
  if (mode === "stop") {
83296
- process5.stdout.write(`${await runConsoleStop()}
83486
+ process6.stdout.write(`${await runConsoleStop()}
83297
83487
  `);
83298
83488
  return;
83299
83489
  }
83300
83490
  if (mode === "restart") {
83301
83491
  await runConsoleRestart();
83302
- process5.stdout.write("Fleet Console restarted.\n");
83492
+ process6.stdout.write("Fleet Console restarted.\n");
83303
83493
  return;
83304
83494
  }
83305
83495
  await openFleetConsole();
83306
- process5.stdout.write("Fleet Console opened.\n");
83496
+ process6.stdout.write("Fleet Console opened.\n");
83307
83497
  }
83308
- async function mainWiki(argv2 = process5.argv.slice(2)) {
83498
+ async function mainWiki(argv2 = process6.argv.slice(2)) {
83309
83499
  if (argv2.includes("--help") || argv2.includes("-h")) {
83310
- process5.stdout.write(`${buildWikiHelpText({ env: process5.env, isTTY: process5.stdout.isTTY })}
83500
+ process6.stdout.write(`${buildWikiHelpText({ env: process6.env, isTTY: process6.stdout.isTTY })}
83311
83501
  `);
83312
83502
  return;
83313
83503
  }
@@ -83319,19 +83509,19 @@ async function mainWiki(argv2 = process5.argv.slice(2)) {
83319
83509
  if (unsupported.length > 0) {
83320
83510
  throw new Error(`Unknown fleet wiki option: ${unsupported[0]}`);
83321
83511
  }
83322
- await openFleetWikiWorkspace({ cwd: process5.cwd() });
83512
+ await openFleetWikiWorkspace({ cwd: process6.cwd() });
83323
83513
  }
83324
83514
  function resolveDefaultServerModulePath() {
83325
83515
  const builtPath = new URL("../dist/cli.mjs", import.meta.url).pathname;
83326
83516
  if (fs5__default.existsSync(builtPath)) return builtPath;
83327
83517
  return fileURLToPath(import.meta.url);
83328
83518
  }
83329
- var isDirectRun = process5.argv[1] && path93__default.resolve(process5.argv[1]) === fileURLToPath(import.meta.url);
83519
+ var isDirectRun = process6.argv[1] && path93__default.resolve(process6.argv[1]) === fileURLToPath(import.meta.url);
83330
83520
  if (isDirectRun) {
83331
83521
  await main().catch((error512) => {
83332
- process5.stderr.write(`${error512 instanceof Error ? error512.message : String(error512)}
83522
+ process6.stderr.write(`${error512 instanceof Error ? error512.message : String(error512)}
83333
83523
  `);
83334
- process5.exitCode = 1;
83524
+ process6.exitCode = 1;
83335
83525
  });
83336
83526
  }
83337
83527
 
@@ -91551,7 +91741,7 @@ function reconcileRuntimeState(carrierRuntime) {
91551
91741
  // src/runtime/workspace-scanner.ts
91552
91742
  import { execFile as execFile3 } from "child_process";
91553
91743
  import { promisify as promisify2 } from "util";
91554
- var execFileAsync3 = promisify2(execFile3);
91744
+ var execFileAsync2 = promisify2(execFile3);
91555
91745
  var GIT_STATUS_TIMEOUT_MS2 = 4e3;
91556
91746
  var GIT_STATUS_MAX_BUFFER2 = 1024 * 1024;
91557
91747
  var GIT_HASH_TIMEOUT_MS2 = 4e3;
@@ -91560,11 +91750,13 @@ function createWorkspaceChangeScanner2() {
91560
91750
  return {
91561
91751
  async snapshot(cwd) {
91562
91752
  try {
91563
- const { stdout } = await execFileAsync3("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
91753
+ const { stdout } = await execFileAsync2("git", ["status", "--porcelain=v1", "-z", "--untracked-files=all"], {
91564
91754
  cwd,
91565
91755
  encoding: "utf8",
91566
91756
  maxBuffer: GIT_STATUS_MAX_BUFFER2,
91567
- timeout: GIT_STATUS_TIMEOUT_MS2
91757
+ timeout: GIT_STATUS_TIMEOUT_MS2,
91758
+ // 콘솔 없는 호스트에서 git.exe 실행 시 콘솔 창이 깜빡이는 것을 방지한다(방어적).
91759
+ windowsHide: true
91568
91760
  });
91569
91761
  const entries = parseGitStatusPorcelainZ2(stdout);
91570
91762
  if (!entries) return null;
@@ -91623,7 +91815,9 @@ function execGitHashObject2(cwd, paths) {
91623
91815
  cwd,
91624
91816
  encoding: "utf8",
91625
91817
  maxBuffer: GIT_HASH_MAX_BUFFER2,
91626
- timeout: GIT_HASH_TIMEOUT_MS2
91818
+ timeout: GIT_HASH_TIMEOUT_MS2,
91819
+ // 콘솔 없는 호스트에서 git.exe 실행 시 콘솔 창이 깜빡이는 것을 방지한다(방어적).
91820
+ windowsHide: true
91627
91821
  },
91628
91822
  (error53, stdout) => {
91629
91823
  if (error53) {