uloop-cli 0.60.0 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5763,7 +5763,7 @@ var import_path3 = require("path");
5763
5763
 
5764
5764
  // src/default-tools.json
5765
5765
  var default_tools_default = {
5766
- version: "0.60.0",
5766
+ version: "0.61.0",
5767
5767
  tools: [
5768
5768
  {
5769
5769
  name: "compile",
@@ -6207,7 +6207,7 @@ function getCachedServerVersion() {
6207
6207
  }
6208
6208
 
6209
6209
  // src/version.ts
6210
- var VERSION = "0.60.0";
6210
+ var VERSION = "0.61.0";
6211
6211
 
6212
6212
  // src/spinner.ts
6213
6213
  var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
@@ -7464,6 +7464,24 @@ var parseCliArgs = (cliArgsString) => {
7464
7464
  }
7465
7465
  return tokens;
7466
7466
  };
7467
+ var groupCliArgs = (args) => {
7468
+ const groups = [];
7469
+ let current = "";
7470
+ for (const arg of args) {
7471
+ if (arg.startsWith("-") && current.length > 0) {
7472
+ groups.push(current);
7473
+ current = arg;
7474
+ } else if (current.length === 0) {
7475
+ current = arg;
7476
+ } else {
7477
+ current += ` ${arg}`;
7478
+ }
7479
+ }
7480
+ if (current.length > 0) {
7481
+ groups.push(current);
7482
+ }
7483
+ return groups;
7484
+ };
7467
7485
  var getProjectCliArgs = async (projectPath) => {
7468
7486
  (0, import_node_assert.default)(projectPath !== null && projectPath !== void 0, "projectPath must not be null");
7469
7487
  const infoFilePath = resolveUnityHubProjectsInfoFile();
@@ -7769,9 +7787,11 @@ async function handleStaleLockfile(projectPath) {
7769
7787
  const message = error instanceof Error ? error.message : String(error);
7770
7788
  console.warn(`Failed to delete UnityLockfile: ${message}`);
7771
7789
  }
7790
+ console.log();
7772
7791
  }
7773
7792
  var KILL_POLL_INTERVAL_MS = 100;
7774
7793
  var KILL_TIMEOUT_MS = 1e4;
7794
+ var GRACEFUL_QUIT_TIMEOUT_MS = 1e4;
7775
7795
  function isProcessAlive(pid) {
7776
7796
  try {
7777
7797
  process.kill(pid, 0);
@@ -7786,9 +7806,9 @@ function killProcess(pid) {
7786
7806
  } catch {
7787
7807
  }
7788
7808
  }
7789
- async function waitForProcessExit(pid) {
7809
+ async function waitForProcessExit(pid, timeoutMs) {
7790
7810
  const start = Date.now();
7791
- while (Date.now() - start < KILL_TIMEOUT_MS) {
7811
+ while (Date.now() - start < timeoutMs) {
7792
7812
  if (!isProcessAlive(pid)) {
7793
7813
  return true;
7794
7814
  }
@@ -7800,16 +7820,73 @@ async function killRunningUnity(projectPath) {
7800
7820
  const processInfo = await findRunningUnityProcess(projectPath);
7801
7821
  if (!processInfo) {
7802
7822
  console.log("No running Unity process found for this project.");
7823
+ console.log();
7803
7824
  return;
7804
7825
  }
7805
7826
  const pid = processInfo.pid;
7806
7827
  console.log(`Killing Unity (PID: ${pid})...`);
7807
7828
  killProcess(pid);
7808
- const exited = await waitForProcessExit(pid);
7829
+ const exited = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
7809
7830
  if (!exited) {
7810
7831
  throw new Error(`Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1e3}s.`);
7811
7832
  }
7812
7833
  console.log("Unity killed.");
7834
+ console.log();
7835
+ }
7836
+ async function sendGracefulQuitMac(pid) {
7837
+ const script = [
7838
+ 'tell application "System Events"',
7839
+ ` set frontmost of (first process whose unix id is ${pid}) to true`,
7840
+ ' keystroke "q" using {command down}',
7841
+ "end tell"
7842
+ ].join("\n");
7843
+ try {
7844
+ await execFileAsync("osascript", ["-e", script]);
7845
+ } catch {
7846
+ }
7847
+ }
7848
+ async function sendGracefulQuitWindows(pid) {
7849
+ const scriptLines = [
7850
+ "$ErrorActionPreference = 'Stop'",
7851
+ `try { $proc = Get-Process -Id ${pid} -ErrorAction Stop; $proc.CloseMainWindow() | Out-Null } catch { }`
7852
+ ];
7853
+ try {
7854
+ await execFileAsync(WINDOWS_POWERSHELL, ["-NoProfile", "-Command", scriptLines.join("\n")]);
7855
+ } catch {
7856
+ }
7857
+ }
7858
+ async function sendGracefulQuit(pid) {
7859
+ if (process.platform === "darwin") {
7860
+ await sendGracefulQuitMac(pid);
7861
+ return;
7862
+ }
7863
+ if (process.platform === "win32") {
7864
+ await sendGracefulQuitWindows(pid);
7865
+ return;
7866
+ }
7867
+ }
7868
+ async function quitRunningUnity(projectPath) {
7869
+ const processInfo = await findRunningUnityProcess(projectPath);
7870
+ if (!processInfo) {
7871
+ console.log("No running Unity process found for this project.");
7872
+ return;
7873
+ }
7874
+ const pid = processInfo.pid;
7875
+ console.log(`Quitting Unity (PID: ${pid})...`);
7876
+ await sendGracefulQuit(pid);
7877
+ console.log(`Sent graceful quit signal. Waiting up to ${GRACEFUL_QUIT_TIMEOUT_MS / 1e3}s...`);
7878
+ const exitedGracefully = await waitForProcessExit(pid, GRACEFUL_QUIT_TIMEOUT_MS);
7879
+ if (exitedGracefully) {
7880
+ console.log("Unity quit gracefully.");
7881
+ return;
7882
+ }
7883
+ console.log("Unity did not respond to graceful quit. Force killing...");
7884
+ killProcess(pid);
7885
+ const exitedAfterKill = await waitForProcessExit(pid, KILL_TIMEOUT_MS);
7886
+ if (!exitedAfterKill) {
7887
+ throw new Error(`Failed to kill Unity (PID: ${pid}) within ${KILL_TIMEOUT_MS / 1e3}s.`);
7888
+ }
7889
+ console.log("Unity force killed.");
7813
7890
  }
7814
7891
  function hasBuildTargetArg(unityArgs) {
7815
7892
  for (const arg of unityArgs) {
@@ -7900,12 +7977,12 @@ function findUnityProjectBfs(rootDir, maxDepth) {
7900
7977
  async function launch(opts) {
7901
7978
  const { projectPath, platform, unityArgs, unityVersion } = opts;
7902
7979
  const unityPath = getUnityPath(unityVersion);
7903
- console.log(`Detected Unity version: ${unityVersion}`);
7904
7980
  if (!(0, import_node_fs2.existsSync)(unityPath)) {
7905
7981
  throw new Error(`Unity ${unityVersion} not found at ${unityPath}. Please install Unity through Unity Hub.`);
7906
7982
  }
7907
7983
  console.log("Opening Unity...");
7908
7984
  console.log(`Project Path: ${projectPath}`);
7985
+ console.log(`Detected Unity version: ${unityVersion}`);
7909
7986
  const args = ["-projectPath", projectPath];
7910
7987
  const unityArgsContainBuildTarget = hasBuildTargetArg(unityArgs);
7911
7988
  if (platform && platform.length > 0 && !unityArgsContainBuildTarget) {
@@ -7913,86 +7990,133 @@ async function launch(opts) {
7913
7990
  }
7914
7991
  const hubCliArgs = await getProjectCliArgs(projectPath);
7915
7992
  if (hubCliArgs.length > 0) {
7993
+ console.log("Unity Hub launch options:");
7994
+ for (const line of groupCliArgs(hubCliArgs)) {
7995
+ console.log(` ${line}`);
7996
+ }
7916
7997
  args.push(...hubCliArgs);
7998
+ } else {
7999
+ console.log("Unity Hub launch options: none");
7917
8000
  }
7918
8001
  if (unityArgs.length > 0) {
7919
8002
  args.push(...unityArgs);
7920
8003
  }
7921
- const child = (0, import_node_child_process.spawn)(unityPath, args, { stdio: "ignore", detached: true });
7922
- child.unref();
7923
- }
7924
-
7925
- // src/commands/launch.ts
7926
- function registerLaunchCommand(program3) {
7927
- program3.command("launch").description("Launch Unity project with matching Editor version").argument("[project-path]", "Path to Unity project").option("-r, --restart", "Kill running Unity and restart").option("-p, --platform <platform>", "Build target (e.g., Android, iOS)").option("--max-depth <n>", "Search depth when project-path is omitted", "3").option("-a, --add-unity-hub", "Add to Unity Hub (does not launch)").option("-f, --favorite", "Add to Unity Hub as favorite (does not launch)").action(async (projectPath, options) => {
7928
- await runLaunchCommand(projectPath, options);
8004
+ return new Promise((resolve5, reject) => {
8005
+ const child = (0, import_node_child_process.spawn)(unityPath, args, {
8006
+ stdio: "ignore",
8007
+ detached: true,
8008
+ // Git Bash (MSYS) がWindows パスをUnix 形式に自動変換するのを防ぐ
8009
+ env: {
8010
+ ...process.env,
8011
+ MSYS_NO_PATHCONV: "1"
8012
+ }
8013
+ });
8014
+ const handleError = (error) => {
8015
+ child.removeListener("spawn", handleSpawn);
8016
+ reject(new Error(`Failed to launch Unity: ${error.message}`));
8017
+ };
8018
+ const handleSpawn = () => {
8019
+ child.removeListener("error", handleError);
8020
+ child.unref();
8021
+ resolve5();
8022
+ };
8023
+ child.once("error", handleError);
8024
+ child.once("spawn", handleSpawn);
7929
8025
  });
7930
8026
  }
7931
- function parseMaxDepth(value) {
7932
- if (value === void 0) {
7933
- return 3;
8027
+ async function orchestrateLaunch(options) {
8028
+ if (options.quit && options.restart) {
8029
+ throw new Error("--quit and --restart cannot be used together.");
7934
8030
  }
7935
- const parsed = parseInt(value, 10);
7936
- if (Number.isNaN(parsed)) {
7937
- console.error(`Error: Invalid --max-depth value: "${value}". Must be an integer.`);
7938
- process.exit(1);
7939
- }
7940
- return parsed;
7941
- }
7942
- async function runLaunchCommand(projectPath, options) {
7943
- const maxDepth = parseMaxDepth(options.maxDepth);
7944
- let resolvedProjectPath = projectPath ? (0, import_path6.resolve)(projectPath) : void 0;
8031
+ let resolvedProjectPath = options.projectPath;
7945
8032
  if (!resolvedProjectPath) {
7946
- const searchRoot = process.cwd();
7947
- const depthInfo = maxDepth === -1 ? "unlimited" : String(maxDepth);
7948
- console.log(
7949
- `No project-path provided. Searching under ${searchRoot} (max-depth: ${depthInfo})...`
7950
- );
7951
- const found = findUnityProjectBfs(searchRoot, maxDepth);
8033
+ const depthInfo = options.searchMaxDepth === -1 ? "unlimited" : String(options.searchMaxDepth);
8034
+ console.log(`Searching for Unity project under ${options.searchRoot} (max-depth: ${depthInfo})...`);
8035
+ const found = findUnityProjectBfs(options.searchRoot, options.searchMaxDepth);
7952
8036
  if (!found) {
7953
- console.error(`Error: Unity project not found under ${searchRoot}.`);
7954
- process.exit(1);
8037
+ throw new Error(`Unity project not found under ${options.searchRoot}.`);
7955
8038
  }
7956
- console.log(`Selected project: ${found}`);
8039
+ console.log();
7957
8040
  resolvedProjectPath = found;
7958
8041
  }
8042
+ if (!(0, import_node_fs2.existsSync)(resolvedProjectPath)) {
8043
+ throw new Error(`Project directory not found: ${resolvedProjectPath}`);
8044
+ }
7959
8045
  const unityVersion = getUnityVersion(resolvedProjectPath);
7960
- const unityHubOnlyMode = options.addUnityHub === true || options.favorite === true;
7961
- if (unityHubOnlyMode) {
8046
+ if (options.addUnityHub || options.favoriteUnityHub) {
7962
8047
  console.log(`Detected Unity version: ${unityVersion}`);
7963
8048
  console.log(`Project Path: ${resolvedProjectPath}`);
7964
8049
  const now2 = /* @__PURE__ */ new Date();
7965
- await ensureProjectEntryAndUpdate(
7966
- resolvedProjectPath,
7967
- unityVersion,
7968
- now2,
7969
- options.favorite === true
7970
- );
8050
+ await ensureProjectEntryAndUpdate(resolvedProjectPath, unityVersion, now2, options.favoriteUnityHub);
7971
8051
  console.log("Unity Hub entry updated.");
7972
- return;
8052
+ return { action: "hub-updated", projectPath: resolvedProjectPath, unityVersion };
8053
+ }
8054
+ if (options.quit) {
8055
+ await quitRunningUnity(resolvedProjectPath);
8056
+ return { action: "quit", projectPath: resolvedProjectPath };
7973
8057
  }
7974
- if (options.restart === true) {
8058
+ const isRestart = options.restart;
8059
+ if (isRestart) {
7975
8060
  await killRunningUnity(resolvedProjectPath);
7976
8061
  } else {
7977
8062
  const runningProcess = await findRunningUnityProcess(resolvedProjectPath);
7978
8063
  if (runningProcess) {
7979
- console.log(
7980
- `Unity process already running for project: ${resolvedProjectPath} (PID: ${runningProcess.pid})`
7981
- );
8064
+ console.log(`Unity process already running for project: ${resolvedProjectPath} (PID: ${runningProcess.pid})`);
7982
8065
  await focusUnityProcess(runningProcess.pid);
7983
- return;
8066
+ return { action: "focused", projectPath: resolvedProjectPath, pid: runningProcess.pid };
7984
8067
  }
7985
8068
  }
7986
8069
  await handleStaleLockfile(resolvedProjectPath);
7987
8070
  const resolved = {
7988
8071
  projectPath: resolvedProjectPath,
7989
8072
  platform: options.platform,
7990
- unityArgs: [],
8073
+ unityArgs: options.unityArgs,
7991
8074
  unityVersion
7992
8075
  };
7993
8076
  await launch(resolved);
7994
8077
  const now = /* @__PURE__ */ new Date();
7995
- await updateLastModifiedIfExists(resolvedProjectPath, now);
8078
+ try {
8079
+ await updateLastModifiedIfExists(resolvedProjectPath, now);
8080
+ } catch (error) {
8081
+ const message = error instanceof Error ? error.message : String(error);
8082
+ console.warn(`Failed to update Unity Hub lastModified: ${message}`);
8083
+ }
8084
+ const action = isRestart ? "killed-and-launched" : "launched";
8085
+ return { action, projectPath: resolvedProjectPath, unityVersion };
8086
+ }
8087
+
8088
+ // src/commands/launch.ts
8089
+ function registerLaunchCommand(program3) {
8090
+ program3.command("launch").description(
8091
+ "Open a Unity project with the matching Editor version installed by Unity Hub.\nAuto-detects project path and Unity version from ProjectSettings/ProjectVersion.txt.\nRun 'uloop launch -h' for all options. Details: https://github.com/hatayama/LaunchUnityCommand"
8092
+ ).argument("[project-path]", "Path to Unity project").option("-r, --restart", "Kill running Unity and restart").option("-q, --quit", "Gracefully quit running Unity").option("-p, --platform <platform>", "Build target (e.g., Android, iOS)").option("--max-depth <n>", "Search depth when project-path is omitted", "3").option("-a, --add-unity-hub", "Add to Unity Hub (does not launch)").option("-f, --favorite", "Add to Unity Hub as favorite (does not launch)").action(async (projectPath, options) => {
8093
+ await runLaunchCommand(projectPath, options);
8094
+ });
8095
+ }
8096
+ function parseMaxDepth(value) {
8097
+ if (value === void 0) {
8098
+ return 3;
8099
+ }
8100
+ const parsed = parseInt(value, 10);
8101
+ if (Number.isNaN(parsed)) {
8102
+ console.error(`Error: Invalid --max-depth value: "${value}". Must be an integer.`);
8103
+ process.exit(1);
8104
+ }
8105
+ return parsed;
8106
+ }
8107
+ async function runLaunchCommand(projectPath, options) {
8108
+ const maxDepth = parseMaxDepth(options.maxDepth);
8109
+ await orchestrateLaunch({
8110
+ projectPath: projectPath ? (0, import_path6.resolve)(projectPath) : void 0,
8111
+ searchRoot: process.cwd(),
8112
+ searchMaxDepth: maxDepth,
8113
+ platform: options.platform,
8114
+ unityArgs: [],
8115
+ restart: options.restart === true,
8116
+ quit: options.quit === true,
8117
+ addUnityHub: options.addUnityHub === true,
8118
+ favoriteUnityHub: options.favorite === true
8119
+ });
7996
8120
  }
7997
8121
 
7998
8122
  // src/commands/focus-window.ts