unity-hub-cli 0.6.1 → 0.8.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.
Files changed (2) hide show
  1. package/dist/index.js +166 -144
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -6,28 +6,42 @@ import { render } from "ink";
6
6
 
7
7
  // src/application/usecases.ts
8
8
  var ListProjectsUseCase = class {
9
- constructor(unityHubProjectsReader, gitRepositoryInfoReader, unityProjectOptionsReader, lockReader) {
9
+ constructor(unityHubProjectsReader, gitRepositoryInfoReader, unityProjectOptionsReader, lockReader, unityProcessReader) {
10
10
  this.unityHubProjectsReader = unityHubProjectsReader;
11
11
  this.gitRepositoryInfoReader = gitRepositoryInfoReader;
12
12
  this.unityProjectOptionsReader = unityProjectOptionsReader;
13
13
  this.lockReader = lockReader;
14
+ this.unityProcessReader = unityProcessReader;
14
15
  }
15
16
  async execute() {
16
17
  const projects = await this.unityHubProjectsReader.listProjects();
17
- const [repositoryInfoResults, lockResults] = await Promise.all([
18
+ const [repositoryInfoResults, lockResults, processResults] = await Promise.all([
18
19
  Promise.allSettled(
19
20
  projects.map((project) => this.gitRepositoryInfoReader.readRepositoryInfo(project.path))
20
21
  ),
21
- Promise.allSettled(projects.map((project) => this.lockReader.isLocked(project.path)))
22
+ Promise.allSettled(projects.map((project) => this.lockReader.isLocked(project.path))),
23
+ Promise.allSettled(projects.map((project) => this.unityProcessReader.findByProjectPath(project.path)))
22
24
  ]);
23
25
  return projects.map((project, index) => {
24
26
  const repositoryResult = repositoryInfoResults[index];
25
27
  const lockResult = lockResults[index];
28
+ const processResult = processResults[index];
26
29
  const repository = repositoryResult.status === "fulfilled" ? repositoryResult.value ?? void 0 : void 0;
27
30
  const isLocked = lockResult.status === "fulfilled" ? Boolean(lockResult.value) : false;
28
- return { project, repository, isLocked };
31
+ const hasRunningProcess = processResult.status === "fulfilled" ? Boolean(processResult.value) : false;
32
+ const launchStatus = this.determineLaunchStatus(hasRunningProcess, isLocked);
33
+ return { project, repository, isLocked, launchStatus };
29
34
  });
30
35
  }
36
+ determineLaunchStatus(hasRunningProcess, isLocked) {
37
+ if (hasRunningProcess) {
38
+ return "running";
39
+ }
40
+ if (isLocked) {
41
+ return "crashed";
42
+ }
43
+ return "idle";
44
+ }
31
45
  };
32
46
  var LaunchCancelledError = class extends Error {
33
47
  constructor() {
@@ -71,24 +85,23 @@ var TerminateProjectUseCase = class {
71
85
  message: "No Unity process is running for this project."
72
86
  };
73
87
  }
74
- const terminated = await this.unityProcessTerminator.terminate(unityProcess);
75
- if (!terminated) {
88
+ const termination = await this.unityProcessTerminator.terminate(unityProcess);
89
+ if (!termination.terminated) {
76
90
  return {
77
91
  terminated: false,
78
92
  message: "Failed to terminate the Unity process."
79
93
  };
80
94
  }
81
- let cleanupMessage = void 0;
82
- try {
83
- await this.unityTempDirectoryCleaner.clean(project.path);
84
- } catch (error) {
85
- const message = error instanceof Error ? error.message : String(error);
86
- console.error("Failed to clean Unity Temp directory:", message);
87
- cleanupMessage = `Unity terminated, but failed to clean Temp: ${message}`;
95
+ if (termination.stage === "sigterm" || termination.stage === "sigkill") {
96
+ try {
97
+ await this.unityTempDirectoryCleaner.clean(project.path);
98
+ } catch (error) {
99
+ const message = error instanceof Error ? error.message : String(error);
100
+ console.error(`Failed to clean Temp directory after termination: ${message}`);
101
+ }
88
102
  }
89
103
  return {
90
- terminated: true,
91
- message: cleanupMessage
104
+ terminated: true
92
105
  };
93
106
  }
94
107
  };
@@ -348,117 +361,14 @@ var UnityHubProjectsReader = class {
348
361
 
349
362
  // src/infrastructure/unityLock.ts
350
363
  import { execFile } from "child_process";
351
- import { constants as constants2, createReadStream, createWriteStream } from "fs";
364
+ import { constants as constants2 } from "fs";
352
365
  import { access as access2, rm } from "fs/promises";
353
366
  import { join as join3 } from "path";
354
- import readline from "readline";
355
367
  import { promisify } from "util";
356
- var RAW_PROMPT_MESSAGE = "Delete UnityLockfile and continue? Type 'y' to continue; anything else aborts: ";
357
368
  var execFileAsync = promisify(execFile);
358
369
  var buildBringToFrontScript = (pid) => {
359
370
  return `tell application "System Events" to set frontmost of (first process whose unix id is ${pid}) to true`;
360
371
  };
361
- var isRawModeSupported = () => {
362
- const stdin = process.stdin;
363
- return Boolean(stdin?.isTTY && typeof stdin.setRawMode === "function" && process.stdout.isTTY);
364
- };
365
- var createPromptInterface = () => {
366
- if (process.stdin.isTTY && process.stdout.isTTY) {
367
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
368
- const close = () => rl.close();
369
- return { rl, close };
370
- }
371
- try {
372
- if (process.platform === "win32") {
373
- const inCandidates = ["\\\\.\\CONIN$", "CONIN$"];
374
- const outCandidates = ["\\\\.\\CONOUT$", "CONOUT$"];
375
- for (const inPath of inCandidates) {
376
- for (const outPath of outCandidates) {
377
- try {
378
- const input = createReadStream(inPath);
379
- const output = createWriteStream(outPath);
380
- const rl = readline.createInterface({ input, output });
381
- const close = () => {
382
- rl.close();
383
- input.destroy();
384
- output.end();
385
- };
386
- return { rl, close };
387
- } catch {
388
- continue;
389
- }
390
- }
391
- }
392
- } else {
393
- const input = createReadStream("/dev/tty");
394
- const output = createWriteStream("/dev/tty");
395
- const rl = readline.createInterface({ input, output });
396
- const close = () => {
397
- rl.close();
398
- input.destroy();
399
- output.end();
400
- };
401
- return { rl, close };
402
- }
403
- } catch {
404
- return void 0;
405
- }
406
- return void 0;
407
- };
408
- var promptYesNoSingleKey = async () => {
409
- const stdin = process.stdin;
410
- const supportsRaw = isRawModeSupported();
411
- const previousRaw = supportsRaw ? stdin.isRaw === true : false;
412
- const wasPaused = stdin.isPaused();
413
- return await new Promise((resolve3) => {
414
- const cleanup = () => {
415
- stdin.removeListener("data", handleData);
416
- if (wasPaused) {
417
- stdin.pause();
418
- }
419
- if (supportsRaw) {
420
- stdin.setRawMode(previousRaw);
421
- }
422
- };
423
- const handleData = (data) => {
424
- const char = data.toString();
425
- const firstByte = data[0] ?? 0;
426
- let result = false;
427
- if (char === "y") {
428
- result = true;
429
- } else if (char === "n" || char === "N" || firstByte === 3 || firstByte === 27 || firstByte === 13) {
430
- result = false;
431
- } else {
432
- result = false;
433
- }
434
- process.stdout.write("\n");
435
- cleanup();
436
- resolve3(result);
437
- };
438
- process.stdout.write(RAW_PROMPT_MESSAGE);
439
- if (supportsRaw) {
440
- stdin.setRawMode(true);
441
- }
442
- if (wasPaused) {
443
- stdin.resume();
444
- }
445
- stdin.once("data", handleData);
446
- });
447
- };
448
- var promptYesNoLine = async () => {
449
- const prompt = createPromptInterface();
450
- if (!prompt) {
451
- console.error("UnityLockfile exists. No interactive console available for confirmation.");
452
- return false;
453
- }
454
- const confirmed = await new Promise((resolve3) => {
455
- prompt.rl.question(RAW_PROMPT_MESSAGE, (answer) => {
456
- resolve3(answer.trim() === "y");
457
- });
458
- });
459
- prompt.close();
460
- return confirmed;
461
- };
462
372
  var pathExists = async (target) => {
463
373
  try {
464
374
  await access2(target, constants2.F_OK);
@@ -468,8 +378,9 @@ var pathExists = async (target) => {
468
378
  }
469
379
  };
470
380
  var UnityLockChecker = class {
471
- constructor(unityProcessReader) {
381
+ constructor(unityProcessReader, tempDirectoryCleaner) {
472
382
  this.unityProcessReader = unityProcessReader;
383
+ this.tempDirectoryCleaner = tempDirectoryCleaner;
473
384
  }
474
385
  async check(projectPath) {
475
386
  const activeProcess = await this.unityProcessReader.findByProjectPath(projectPath);
@@ -485,15 +396,22 @@ var UnityLockChecker = class {
485
396
  if (!hasLockfile) {
486
397
  return "allow";
487
398
  }
488
- console.log(`UnityLockfile found: ${lockfilePath}`);
489
- console.log("Another Unity process may be using this project.");
490
- const confirmed = isRawModeSupported() ? await promptYesNoSingleKey() : await promptYesNoLine();
491
- if (!confirmed) {
492
- console.log("Aborted by user.");
493
- return "skip";
399
+ console.log(`UnityLockfile found without active Unity process: ${lockfilePath}`);
400
+ console.log("Assuming previous crash. Cleaning Temp directory and continuing launch.");
401
+ try {
402
+ await this.tempDirectoryCleaner.clean(projectPath);
403
+ } catch (error) {
404
+ const message = error instanceof Error ? error.message : String(error);
405
+ console.error(`Failed to clean Temp directory: ${message}`);
494
406
  }
495
- await rm(lockfilePath, { force: true });
496
- console.log("Deleted UnityLockfile. Continuing launch.");
407
+ try {
408
+ await rm(lockfilePath, { force: true });
409
+ console.log("Deleted UnityLockfile.");
410
+ } catch (error) {
411
+ const message = error instanceof Error ? error.message : String(error);
412
+ console.error(`Failed to delete UnityLockfile: ${message}`);
413
+ }
414
+ console.log("Continuing launch.");
497
415
  return "allow";
498
416
  }
499
417
  async bringUnityToFront(pid) {
@@ -527,6 +445,8 @@ var PROCESS_LIST_ARGS = ["-axo", "pid=,command=", "-ww"];
527
445
  var PROCESS_LIST_COMMAND = "ps";
528
446
  var TERMINATE_TIMEOUT_MILLIS = 5e3;
529
447
  var TERMINATE_POLL_INTERVAL_MILLIS = 200;
448
+ var GRACEFUL_QUIT_TIMEOUT_MILLIS = 3e3;
449
+ var GRACEFUL_QUIT_POLL_INTERVAL_MILLIS = 200;
530
450
  var delay = async (duration) => {
531
451
  await new Promise((resolveDelay) => {
532
452
  setTimeout(() => {
@@ -626,11 +546,33 @@ var MacUnityProcessReader = class {
626
546
  };
627
547
  var MacUnityProcessTerminator = class {
628
548
  async terminate(unityProcess) {
549
+ let attemptedGraceful = false;
550
+ if (process.platform === "darwin") {
551
+ attemptedGraceful = true;
552
+ try {
553
+ const script = [
554
+ 'tell application "System Events"',
555
+ ` set frontmost of (first process whose unix id is ${unityProcess.pid}) to true`,
556
+ ' keystroke "q" using {command down}',
557
+ "end tell"
558
+ ].join("\n");
559
+ await execFileAsync2("osascript", ["-e", script]);
560
+ const deadlineGraceful = Date.now() + GRACEFUL_QUIT_TIMEOUT_MILLIS;
561
+ while (Date.now() < deadlineGraceful) {
562
+ await delay(GRACEFUL_QUIT_POLL_INTERVAL_MILLIS);
563
+ const alive = ensureProcessAlive(unityProcess.pid);
564
+ if (!alive) {
565
+ return { terminated: true, stage: "graceful" };
566
+ }
567
+ }
568
+ } catch {
569
+ }
570
+ }
629
571
  try {
630
572
  process.kill(unityProcess.pid, "SIGTERM");
631
573
  } catch (error) {
632
574
  if (isProcessMissingError(error)) {
633
- return false;
575
+ return { terminated: true, stage: attemptedGraceful ? "graceful" : "sigterm" };
634
576
  }
635
577
  throw new Error(
636
578
  `Failed to terminate the Unity process (PID: ${unityProcess.pid}): ${error instanceof Error ? error.message : String(error)}`
@@ -641,21 +583,22 @@ var MacUnityProcessTerminator = class {
641
583
  await delay(TERMINATE_POLL_INTERVAL_MILLIS);
642
584
  const alive = ensureProcessAlive(unityProcess.pid);
643
585
  if (!alive) {
644
- return true;
586
+ return { terminated: true, stage: "sigterm" };
645
587
  }
646
588
  }
647
589
  try {
648
590
  process.kill(unityProcess.pid, "SIGKILL");
649
591
  } catch (error) {
650
592
  if (isProcessMissingError(error)) {
651
- return true;
593
+ return { terminated: true, stage: "sigkill" };
652
594
  }
653
595
  throw new Error(
654
596
  `Failed to forcefully terminate the Unity process (PID: ${unityProcess.pid}): ${error instanceof Error ? error.message : String(error)}`
655
597
  );
656
598
  }
657
599
  await delay(TERMINATE_POLL_INTERVAL_MILLIS);
658
- return !ensureProcessAlive(unityProcess.pid);
600
+ const aliveAfterKill = ensureProcessAlive(unityProcess.pid);
601
+ return aliveAfterKill ? { terminated: false } : { terminated: true, stage: "sigkill" };
659
602
  }
660
603
  };
661
604
 
@@ -764,12 +707,16 @@ var formatUpdatedText = (lastModified) => {
764
707
  var homeDirectory = process.env.HOME ?? "";
765
708
  var homePrefix = homeDirectory ? `${homeDirectory}/` : "";
766
709
  var minimumVisibleProjectCount = 4;
767
- var defaultHintMessage = "Move with arrows or j/k \xB7 Launch with o \xB7 Quit Unity with q \xB7 Copy cd path with c";
710
+ var defaultHintMessage = "Move with arrows or j/k \xB7 Launch with o \xB7 Quit Unity with q \xB7 Refresh with r \xB7 Copy cd path with c";
768
711
  var PROJECT_COLOR = "#abd8e7";
769
712
  var BRANCH_COLOR = "#e3839c";
770
713
  var PATH_COLOR = "#719bd8";
771
714
  var LOCK_COLOR = "yellow";
772
- var LOCK_LABEL = "[running]";
715
+ var STATUS_LABELS = {
716
+ idle: "",
717
+ running: "[running]",
718
+ crashed: "[crash]"
719
+ };
773
720
  var shortenHomePath = (targetPath) => {
774
721
  if (!homeDirectory) {
775
722
  return targetPath;
@@ -790,18 +737,21 @@ var App = ({
790
737
  projects,
791
738
  onLaunch,
792
739
  onTerminate,
740
+ onRefresh,
793
741
  useGitRootName = true,
794
742
  showBranch = true,
795
743
  showPath = true
796
744
  }) => {
797
745
  const { exit } = useApp();
798
746
  const { stdout } = useStdout();
747
+ const [projectViews, setProjectViews] = useState(projects);
799
748
  const [visibleCount, setVisibleCount] = useState(minimumVisibleProjectCount);
800
749
  const [index, setIndex] = useState(0);
801
750
  const [hint, setHint] = useState(defaultHintMessage);
802
751
  const [windowStart, setWindowStart] = useState(0);
803
752
  const [releasedProjects, setReleasedProjects] = useState(/* @__PURE__ */ new Set());
804
753
  const [launchedProjects, setLaunchedProjects] = useState(/* @__PURE__ */ new Set());
754
+ const [isRefreshing, setIsRefreshing] = useState(false);
805
755
  const linesPerProject = (showBranch ? 1 : 0) + (showPath ? 1 : 0) + 2;
806
756
  const sortedProjects = useMemo(() => {
807
757
  const fallbackTime = 0;
@@ -815,7 +765,7 @@ var App = ({
815
765
  return view.project.title.toLocaleLowerCase();
816
766
  };
817
767
  const toTieBreaker = (view) => view.project.path.toLocaleLowerCase();
818
- return [...projects].sort((a, b) => {
768
+ return [...projectViews].sort((a, b) => {
819
769
  if (a.project.favorite !== b.project.favorite) {
820
770
  return a.project.favorite ? -1 : 1;
821
771
  }
@@ -831,7 +781,7 @@ var App = ({
831
781
  }
832
782
  return keyA.localeCompare(keyB);
833
783
  });
834
- }, [projects, useGitRootName]);
784
+ }, [projectViews, useGitRootName]);
835
785
  useEffect(() => {
836
786
  const handleSigint = () => {
837
787
  exit();
@@ -1050,9 +1000,63 @@ var App = ({
1050
1000
  }
1051
1001
  }, [index, onTerminate, sortedProjects]);
1052
1002
  useEffect(() => {
1003
+ setProjectViews(projects);
1053
1004
  setReleasedProjects(/* @__PURE__ */ new Set());
1054
1005
  setLaunchedProjects(/* @__PURE__ */ new Set());
1055
1006
  }, [projects]);
1007
+ const refreshProjects = useCallback(async () => {
1008
+ if (!onRefresh) {
1009
+ setHint("Refresh not available");
1010
+ setTimeout(() => {
1011
+ setHint(defaultHintMessage);
1012
+ }, 2e3);
1013
+ return;
1014
+ }
1015
+ if (isRefreshing) {
1016
+ setHint("Already refreshing");
1017
+ setTimeout(() => {
1018
+ setHint(defaultHintMessage);
1019
+ }, 2e3);
1020
+ return;
1021
+ }
1022
+ setIsRefreshing(true);
1023
+ setHint("Refreshing projects...");
1024
+ try {
1025
+ const updatedProjects = await onRefresh();
1026
+ setProjectViews(updatedProjects);
1027
+ setReleasedProjects(/* @__PURE__ */ new Set());
1028
+ setLaunchedProjects(/* @__PURE__ */ new Set());
1029
+ setIndex((previousIndex) => {
1030
+ if (updatedProjects.length === 0) {
1031
+ return 0;
1032
+ }
1033
+ const previousProject = sortedProjects[previousIndex]?.project;
1034
+ if (!previousProject) {
1035
+ return Math.min(previousIndex, updatedProjects.length - 1);
1036
+ }
1037
+ const nextIndex = updatedProjects.findIndex(
1038
+ (candidate) => candidate.project.id === previousProject.id
1039
+ );
1040
+ if (nextIndex === -1) {
1041
+ return Math.min(previousIndex, updatedProjects.length - 1);
1042
+ }
1043
+ return nextIndex;
1044
+ });
1045
+ setWindowStart(0);
1046
+ setHint("Project list refreshed");
1047
+ setTimeout(() => {
1048
+ setHint(defaultHintMessage);
1049
+ }, 2e3);
1050
+ } catch (error) {
1051
+ const message = error instanceof Error ? error.message : String(error);
1052
+ setHint(`Failed to refresh: ${message}`);
1053
+ setTimeout(() => {
1054
+ setHint(defaultHintMessage);
1055
+ }, 3e3);
1056
+ } finally {
1057
+ setIsRefreshing(false);
1058
+ }
1059
+ }, [isRefreshing, onRefresh, sortedProjects]);
1056
1060
  useInput((input, key) => {
1057
1061
  if (input === "j" || key.downArrow) {
1058
1062
  move(1);
@@ -1068,6 +1072,10 @@ var App = ({
1068
1072
  void launchSelected();
1069
1073
  return;
1070
1074
  }
1075
+ if (input === "r") {
1076
+ void refreshProjects();
1077
+ return;
1078
+ }
1071
1079
  if (input === "c") {
1072
1080
  copyProjectPath();
1073
1081
  }
@@ -1088,7 +1096,7 @@ var App = ({
1088
1096
  };
1089
1097
  }, [limit, sortedProjects, windowStart]);
1090
1098
  const scrollbarChars = useMemo(() => {
1091
- const totalProjects = projects.length;
1099
+ const totalProjects = projectViews.length;
1092
1100
  const totalLines = totalProjects * linesPerProject;
1093
1101
  const windowProjects = visibleProjects.length;
1094
1102
  const visibleLines = windowProjects * linesPerProject;
@@ -1113,9 +1121,9 @@ var App = ({
1113
1121
  }
1114
1122
  return "|";
1115
1123
  });
1116
- }, [linesPerProject, projects.length, startIndex, visibleProjects]);
1124
+ }, [linesPerProject, projectViews.length, startIndex, visibleProjects]);
1117
1125
  const rows = useMemo(() => {
1118
- return visibleProjects.map(({ project, repository, isLocked }, offset) => {
1126
+ return visibleProjects.map(({ project, repository, launchStatus }, offset) => {
1119
1127
  const rowIndex = startIndex + offset;
1120
1128
  const isSelected = rowIndex === index;
1121
1129
  const arrow = isSelected ? ">" : " ";
@@ -1124,12 +1132,24 @@ var App = ({
1124
1132
  const updatedText = formatUpdatedText(project.lastModified);
1125
1133
  const pathLine = shortenHomePath(project.path);
1126
1134
  const branchLine = formatBranch(repository?.branch);
1127
- const activeLock = isLocked && !releasedProjects.has(project.id) || launchedProjects.has(project.id);
1135
+ const hasReleasedLock = releasedProjects.has(project.id);
1136
+ const isLocallyLaunched = launchedProjects.has(project.id);
1137
+ const displayStatus = (() => {
1138
+ if (isLocallyLaunched) {
1139
+ return "running";
1140
+ }
1141
+ if (hasReleasedLock) {
1142
+ return "idle";
1143
+ }
1144
+ return launchStatus;
1145
+ })();
1128
1146
  const baseScrollbarIndex = offset * linesPerProject;
1129
1147
  const titleScrollbar = scrollbarChars[baseScrollbarIndex] ?? " ";
1130
1148
  const branchScrollbar = showBranch ? scrollbarChars[baseScrollbarIndex + 1] ?? " " : " ";
1131
1149
  const pathScrollbar = showPath ? scrollbarChars[baseScrollbarIndex + 1 + (showBranch ? 1 : 0)] ?? " " : " ";
1132
1150
  const spacerScrollbar = scrollbarChars[baseScrollbarIndex + linesPerProject - 1] ?? " ";
1151
+ const statusLabel = STATUS_LABELS[displayStatus];
1152
+ const statusColor = displayStatus === "running" ? LOCK_COLOR : displayStatus === "crashed" ? "red" : void 0;
1133
1153
  return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", children: [
1134
1154
  /* @__PURE__ */ jsxs(Box, { flexGrow: 1, flexDirection: "column", children: [
1135
1155
  /* @__PURE__ */ jsxs(Text, { children: [
@@ -1143,7 +1163,7 @@ var App = ({
1143
1163
  versionLabel
1144
1164
  ] }),
1145
1165
  updatedText ? /* @__PURE__ */ jsx(Text, { color: isSelected ? "green" : void 0, children: ` ${updatedText}` }) : null,
1146
- activeLock ? /* @__PURE__ */ jsx(Text, { color: LOCK_COLOR, children: ` ${LOCK_LABEL}` }) : null
1166
+ statusLabel && statusColor ? /* @__PURE__ */ jsx(Text, { color: statusColor, children: ` ${statusLabel}` }) : null
1147
1167
  ] }),
1148
1168
  showBranch ? /* @__PURE__ */ jsxs(Text, { color: isSelected ? "green" : BRANCH_COLOR, children: [
1149
1169
  " ",
@@ -1187,17 +1207,18 @@ var bootstrap = async () => {
1187
1207
  const gitRepositoryInfoReader = new GitRepositoryInfoReader();
1188
1208
  const lockStatusReader = new UnityLockStatusReader();
1189
1209
  const unityProcessReader = new MacUnityProcessReader();
1210
+ const unityTempDirectoryCleaner = new UnityTempDirectoryCleaner();
1190
1211
  const listProjectsUseCase = new ListProjectsUseCase(
1191
1212
  unityHubReader,
1192
1213
  gitRepositoryInfoReader,
1193
1214
  unityHubReader,
1194
- lockStatusReader
1215
+ lockStatusReader,
1216
+ unityProcessReader
1195
1217
  );
1196
1218
  const editorPathResolver = new MacEditorPathResolver();
1197
1219
  const processLauncher = new NodeProcessLauncher();
1198
- const lockChecker = new UnityLockChecker(unityProcessReader);
1220
+ const lockChecker = new UnityLockChecker(unityProcessReader, unityTempDirectoryCleaner);
1199
1221
  const unityProcessTerminator = new MacUnityProcessTerminator();
1200
- const unityTempDirectoryCleaner = new UnityTempDirectoryCleaner();
1201
1222
  const launchProjectUseCase = new LaunchProjectUseCase(
1202
1223
  editorPathResolver,
1203
1224
  processLauncher,
@@ -1222,6 +1243,7 @@ var bootstrap = async () => {
1222
1243
  projects,
1223
1244
  onLaunch: (project) => launchProjectUseCase.execute(project),
1224
1245
  onTerminate: (project) => terminateProjectUseCase.execute(project),
1246
+ onRefresh: () => listProjectsUseCase.execute(),
1225
1247
  useGitRootName,
1226
1248
  showBranch,
1227
1249
  showPath
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unity-hub-cli",
3
- "version": "0.6.1",
3
+ "version": "0.8.0",
4
4
  "description": "A CLI tool that reads Unity Hub's projects and launches Unity Editor with an interactive TUI",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {