unity-hub-cli 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/index.js +343 -13
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ On standalone Git Bash (MinTTY), raw mode is not supported; use PowerShell/CMD/W
|
|
|
46
46
|
| `i` | Launch external editor only |
|
|
47
47
|
| `q` | Quit Unity for selected project |
|
|
48
48
|
| `r` | Refresh project list |
|
|
49
|
-
| `c` | Copy project path to clipboard |
|
|
49
|
+
| `c` | Copy project path to clipboard (when launched via the `unity-hub` shell function, it will `cd` into the project directory and exit) |
|
|
50
50
|
| `s` | Open sort settings panel |
|
|
51
51
|
| `v` | Open visibility settings panel |
|
|
52
52
|
| `Ctrl + C` | Exit |
|
|
@@ -60,6 +60,7 @@ The display includes Git branch (if present), Unity version, project path, and l
|
|
|
60
60
|
- `--no-git-root-name`: Display Unity project titles instead of Git repository root folder names.
|
|
61
61
|
- `--shell-init`: Install shell function for automatic `cd` integration (with confirmation prompt).
|
|
62
62
|
- `--shell-init --dry-run`: Preview the shell function without installing.
|
|
63
|
+
- `update`: Update the globally installed `unity-hub-cli` to the latest version (with a confirmation prompt). It runs: `npm install -g unity-hub-cli@latest --ignore-scripts --no-fund`.
|
|
63
64
|
|
|
64
65
|
## Shell Integration
|
|
65
66
|
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.tsx
|
|
4
|
-
import { execSync } from "child_process";
|
|
4
|
+
import { execSync, spawnSync } from "child_process";
|
|
5
5
|
import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
import { dirname as dirname2, join as join9 } from "path";
|
|
8
8
|
import process3 from "process";
|
|
9
9
|
import { createInterface as createInterface2 } from "readline";
|
|
10
|
+
import { fileURLToPath } from "url";
|
|
10
11
|
import chalk from "chalk";
|
|
11
12
|
import { render } from "ink";
|
|
12
13
|
|
|
@@ -784,6 +785,38 @@ var MacUnityHubProjectsReader = class {
|
|
|
784
785
|
};
|
|
785
786
|
await writeFile(HUB_PROJECTS_PATH, JSON.stringify(json, void 0, 2), "utf8");
|
|
786
787
|
}
|
|
788
|
+
async toggleFavorite(projectPath) {
|
|
789
|
+
let content;
|
|
790
|
+
try {
|
|
791
|
+
content = await readFile2(HUB_PROJECTS_PATH, "utf8");
|
|
792
|
+
} catch {
|
|
793
|
+
throw new Error(`Unity Hub project list not found (${HUB_PROJECTS_PATH}).`);
|
|
794
|
+
}
|
|
795
|
+
let json;
|
|
796
|
+
try {
|
|
797
|
+
json = JSON.parse(content);
|
|
798
|
+
} catch {
|
|
799
|
+
throw new Error("Unable to read the Unity Hub project list (permissions/format error).");
|
|
800
|
+
}
|
|
801
|
+
if (!json.data) {
|
|
802
|
+
return false;
|
|
803
|
+
}
|
|
804
|
+
const projectKey = Object.keys(json.data).find((key) => json.data?.[key]?.path === projectPath);
|
|
805
|
+
if (!projectKey) {
|
|
806
|
+
return false;
|
|
807
|
+
}
|
|
808
|
+
const original = json.data[projectKey];
|
|
809
|
+
if (!original) {
|
|
810
|
+
return false;
|
|
811
|
+
}
|
|
812
|
+
const newFavorite = original.isFavorite !== true;
|
|
813
|
+
json.data[projectKey] = {
|
|
814
|
+
...original,
|
|
815
|
+
isFavorite: newFavorite
|
|
816
|
+
};
|
|
817
|
+
await writeFile(HUB_PROJECTS_PATH, JSON.stringify(json, void 0, 2), "utf8");
|
|
818
|
+
return newFavorite;
|
|
819
|
+
}
|
|
787
820
|
async readCliArgs(projectPath) {
|
|
788
821
|
const infoPath = `${process.env.HOME ?? ""}/Library/Application Support/UnityHub/projectsInfo.json`;
|
|
789
822
|
let content;
|
|
@@ -915,6 +948,38 @@ var WinUnityHubProjectsReader = class {
|
|
|
915
948
|
};
|
|
916
949
|
await writeFile2(HUB_PROJECTS_PATH2, JSON.stringify(json, void 0, 2), "utf8");
|
|
917
950
|
}
|
|
951
|
+
async toggleFavorite(projectPath) {
|
|
952
|
+
let content;
|
|
953
|
+
try {
|
|
954
|
+
content = await readFile3(HUB_PROJECTS_PATH2, "utf8");
|
|
955
|
+
} catch {
|
|
956
|
+
throw new Error(`Unity Hub project list not found (${HUB_PROJECTS_PATH2}).`);
|
|
957
|
+
}
|
|
958
|
+
let json;
|
|
959
|
+
try {
|
|
960
|
+
json = JSON.parse(content);
|
|
961
|
+
} catch {
|
|
962
|
+
throw new Error("Unable to read the Unity Hub project list (permissions/format error).");
|
|
963
|
+
}
|
|
964
|
+
if (!json.data) {
|
|
965
|
+
return false;
|
|
966
|
+
}
|
|
967
|
+
const projectKey = Object.keys(json.data).find((key) => json.data?.[key]?.path === projectPath);
|
|
968
|
+
if (!projectKey) {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
const original = json.data[projectKey];
|
|
972
|
+
if (!original) {
|
|
973
|
+
return false;
|
|
974
|
+
}
|
|
975
|
+
const newFavorite = original.isFavorite !== true;
|
|
976
|
+
json.data[projectKey] = {
|
|
977
|
+
...original,
|
|
978
|
+
isFavorite: newFavorite
|
|
979
|
+
};
|
|
980
|
+
await writeFile2(HUB_PROJECTS_PATH2, JSON.stringify(json, void 0, 2), "utf8");
|
|
981
|
+
return newFavorite;
|
|
982
|
+
}
|
|
918
983
|
async readCliArgs(projectPath) {
|
|
919
984
|
const infoPath = join6(HUB_DIR, "projectsInfo.json");
|
|
920
985
|
let content;
|
|
@@ -1673,7 +1738,8 @@ var ProjectList = ({
|
|
|
1673
1738
|
const rowIndex = startIndex + offset;
|
|
1674
1739
|
const isSelected = rowIndex === selectedIndex;
|
|
1675
1740
|
const selectionBar = isSelected ? "\u2503" : " ";
|
|
1676
|
-
const
|
|
1741
|
+
const baseName = formatProjectName(project.title, repository, useGitRootName);
|
|
1742
|
+
const projectName = project.favorite ? `\u2B51 ${baseName}` : baseName;
|
|
1677
1743
|
const versionLabel = `(${project.version.value})`;
|
|
1678
1744
|
const updatedText = formatUpdatedText(project.lastModified);
|
|
1679
1745
|
const pathLine = shortenHomePath(project.path);
|
|
@@ -1993,11 +2059,11 @@ var extractRootFolder2 = (repository) => {
|
|
|
1993
2059
|
return base || void 0;
|
|
1994
2060
|
};
|
|
1995
2061
|
var minimumVisibleProjectCount = 4;
|
|
1996
|
-
var
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
return root && root.length > 0 ? root : view.project.path;
|
|
2062
|
+
var buildDefaultHintMessage = (outputPathOnExit) => {
|
|
2063
|
+
const cLabel = outputPathOnExit ? "cd" : "copy";
|
|
2064
|
+
return `j/k Select \xB7 [o]pen [O]+Editor [i]de [q]uit [r]efresh [c]${cLabel} [f]av [s]ort [v]isibility \xB7 ^C Exit`;
|
|
2000
2065
|
};
|
|
2066
|
+
var getCopyTargetPath = (view) => view.project.path;
|
|
2001
2067
|
var App = ({
|
|
2002
2068
|
projects,
|
|
2003
2069
|
onLaunch,
|
|
@@ -2005,6 +2071,7 @@ var App = ({
|
|
|
2005
2071
|
onLaunchEditorOnly,
|
|
2006
2072
|
onTerminate,
|
|
2007
2073
|
onRefresh,
|
|
2074
|
+
onToggleFavorite,
|
|
2008
2075
|
useGitRootName = true,
|
|
2009
2076
|
outputPathOnExit = false,
|
|
2010
2077
|
onSetExitPath
|
|
@@ -2012,6 +2079,10 @@ var App = ({
|
|
|
2012
2079
|
const { exit } = useApp();
|
|
2013
2080
|
const { stdout } = useStdout2();
|
|
2014
2081
|
const colors = useThemeColors();
|
|
2082
|
+
const defaultHintMessage = useMemo2(
|
|
2083
|
+
() => buildDefaultHintMessage(outputPathOnExit),
|
|
2084
|
+
[outputPathOnExit]
|
|
2085
|
+
);
|
|
2015
2086
|
const [projectViews, setProjectViews] = useState4(projects);
|
|
2016
2087
|
const [isSortMenuOpen, setIsSortMenuOpen] = useState4(false);
|
|
2017
2088
|
const [isVisibilityMenuOpen, setIsVisibilityMenuOpen] = useState4(false);
|
|
@@ -2172,15 +2243,21 @@ var App = ({
|
|
|
2172
2243
|
});
|
|
2173
2244
|
}, [index, limit, sortedProjects.length]);
|
|
2174
2245
|
const copyProjectPath = useCallback(() => {
|
|
2246
|
+
const actionLabel = outputPathOnExit ? "cd" : "copy";
|
|
2175
2247
|
const projectView = sortedProjects[index];
|
|
2176
2248
|
const projectPath = projectView ? getCopyTargetPath(projectView) : void 0;
|
|
2177
2249
|
if (!projectPath) {
|
|
2178
|
-
setHint(
|
|
2250
|
+
setHint(`No project to ${actionLabel}`);
|
|
2179
2251
|
setTimeout(() => {
|
|
2180
2252
|
setHint(defaultHintMessage);
|
|
2181
2253
|
}, 2e3);
|
|
2182
2254
|
return;
|
|
2183
2255
|
}
|
|
2256
|
+
if (outputPathOnExit) {
|
|
2257
|
+
onSetExitPath?.(projectPath);
|
|
2258
|
+
exit();
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2184
2261
|
try {
|
|
2185
2262
|
const command = buildCdCommand(projectPath);
|
|
2186
2263
|
clipboard.writeSync(command);
|
|
@@ -2193,7 +2270,7 @@ var App = ({
|
|
|
2193
2270
|
setTimeout(() => {
|
|
2194
2271
|
setHint(defaultHintMessage);
|
|
2195
2272
|
}, 2e3);
|
|
2196
|
-
}, [index, sortedProjects]);
|
|
2273
|
+
}, [defaultHintMessage, exit, index, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2197
2274
|
const launchSelected = useCallback(async () => {
|
|
2198
2275
|
const projectView = sortedProjects[index];
|
|
2199
2276
|
if (!projectView) {
|
|
@@ -2248,7 +2325,7 @@ var App = ({
|
|
|
2248
2325
|
setHint(defaultHintMessage);
|
|
2249
2326
|
}, 3e3);
|
|
2250
2327
|
}
|
|
2251
|
-
}, [exit, index, onLaunch, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2328
|
+
}, [defaultHintMessage, exit, index, onLaunch, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2252
2329
|
const launchSelectedWithEditor = useCallback(async () => {
|
|
2253
2330
|
if (!onLaunchWithEditor) {
|
|
2254
2331
|
setHint("Launch with editor not available");
|
|
@@ -2310,7 +2387,7 @@ var App = ({
|
|
|
2310
2387
|
setHint(defaultHintMessage);
|
|
2311
2388
|
}, 3e3);
|
|
2312
2389
|
}
|
|
2313
|
-
}, [exit, index, onLaunchWithEditor, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2390
|
+
}, [defaultHintMessage, exit, index, onLaunchWithEditor, onSetExitPath, outputPathOnExit, sortedProjects]);
|
|
2314
2391
|
const launchEditorOnly = useCallback(async () => {
|
|
2315
2392
|
if (!onLaunchEditorOnly) {
|
|
2316
2393
|
setHint("Launch editor only not available");
|
|
@@ -2341,7 +2418,7 @@ var App = ({
|
|
|
2341
2418
|
setHint(defaultHintMessage);
|
|
2342
2419
|
}, 3e3);
|
|
2343
2420
|
}
|
|
2344
|
-
}, [index, onLaunchEditorOnly, sortedProjects]);
|
|
2421
|
+
}, [defaultHintMessage, index, onLaunchEditorOnly, sortedProjects]);
|
|
2345
2422
|
const terminateSelected = useCallback(async () => {
|
|
2346
2423
|
const projectView = sortedProjects[index];
|
|
2347
2424
|
if (!projectView) {
|
|
@@ -2384,7 +2461,7 @@ var App = ({
|
|
|
2384
2461
|
setHint(defaultHintMessage);
|
|
2385
2462
|
}, 3e3);
|
|
2386
2463
|
}
|
|
2387
|
-
}, [index, onTerminate, sortedProjects]);
|
|
2464
|
+
}, [defaultHintMessage, index, onTerminate, sortedProjects]);
|
|
2388
2465
|
useEffect4(() => {
|
|
2389
2466
|
setProjectViews(projects);
|
|
2390
2467
|
setReleasedProjects(/* @__PURE__ */ new Set());
|
|
@@ -2442,7 +2519,44 @@ var App = ({
|
|
|
2442
2519
|
} finally {
|
|
2443
2520
|
setIsRefreshing(false);
|
|
2444
2521
|
}
|
|
2445
|
-
}, [isRefreshing, onRefresh, sortedProjects]);
|
|
2522
|
+
}, [defaultHintMessage, isRefreshing, onRefresh, sortedProjects]);
|
|
2523
|
+
const toggleFavoriteSelected = useCallback(async () => {
|
|
2524
|
+
if (!onToggleFavorite) {
|
|
2525
|
+
setHint("Toggle favorite not available");
|
|
2526
|
+
setTimeout(() => {
|
|
2527
|
+
setHint(defaultHintMessage);
|
|
2528
|
+
}, 2e3);
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
const projectView = sortedProjects[index];
|
|
2532
|
+
if (!projectView) {
|
|
2533
|
+
setHint("No project to toggle favorite");
|
|
2534
|
+
setTimeout(() => {
|
|
2535
|
+
setHint(defaultHintMessage);
|
|
2536
|
+
}, 2e3);
|
|
2537
|
+
return;
|
|
2538
|
+
}
|
|
2539
|
+
const { project } = projectView;
|
|
2540
|
+
try {
|
|
2541
|
+
const newFavorite = await onToggleFavorite(project);
|
|
2542
|
+
setProjectViews(
|
|
2543
|
+
(prev) => prev.map(
|
|
2544
|
+
(pv) => pv.project.id === project.id ? { ...pv, project: { ...pv.project, favorite: newFavorite } } : pv
|
|
2545
|
+
)
|
|
2546
|
+
);
|
|
2547
|
+
const status = newFavorite ? "added to" : "removed from";
|
|
2548
|
+
setHint(`${project.title} ${status} favorites`);
|
|
2549
|
+
setTimeout(() => {
|
|
2550
|
+
setHint(defaultHintMessage);
|
|
2551
|
+
}, 2e3);
|
|
2552
|
+
} catch (error) {
|
|
2553
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2554
|
+
setHint(`Failed to toggle favorite: ${message}`);
|
|
2555
|
+
setTimeout(() => {
|
|
2556
|
+
setHint(defaultHintMessage);
|
|
2557
|
+
}, 3e3);
|
|
2558
|
+
}
|
|
2559
|
+
}, [defaultHintMessage, index, onToggleFavorite, sortedProjects]);
|
|
2446
2560
|
useInput((input, key) => {
|
|
2447
2561
|
if (isSortMenuOpen) {
|
|
2448
2562
|
if (key.escape || input === "\x1B") {
|
|
@@ -2563,6 +2677,10 @@ var App = ({
|
|
|
2563
2677
|
}
|
|
2564
2678
|
if (input === "c") {
|
|
2565
2679
|
copyProjectPath();
|
|
2680
|
+
return;
|
|
2681
|
+
}
|
|
2682
|
+
if (input === "f" || input === "F") {
|
|
2683
|
+
void toggleFavoriteSelected();
|
|
2566
2684
|
}
|
|
2567
2685
|
});
|
|
2568
2686
|
const { startIndex, visibleProjects } = useMemo2(() => {
|
|
@@ -2642,6 +2760,12 @@ var App = ({
|
|
|
2642
2760
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
2643
2761
|
var SHELL_INIT_MARKER_START = "# >>> unity-hub-cli >>>";
|
|
2644
2762
|
var SHELL_INIT_MARKER_END = "# <<< unity-hub-cli <<<";
|
|
2763
|
+
var getVersion = () => {
|
|
2764
|
+
const currentDir = dirname2(fileURLToPath(import.meta.url));
|
|
2765
|
+
const packageJsonPath = join9(currentDir, "..", "package.json");
|
|
2766
|
+
const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
|
|
2767
|
+
return packageJson.version;
|
|
2768
|
+
};
|
|
2645
2769
|
var getShellConfigPath = () => {
|
|
2646
2770
|
const shell = process3.env["SHELL"] ?? "";
|
|
2647
2771
|
const home = homedir();
|
|
@@ -2743,6 +2867,139 @@ var getUnityHubCliPath = () => {
|
|
|
2743
2867
|
}
|
|
2744
2868
|
return "unity-hub-cli";
|
|
2745
2869
|
};
|
|
2870
|
+
var getNpmCommand = () => {
|
|
2871
|
+
return process3.platform === "win32" ? "npm.cmd" : "npm";
|
|
2872
|
+
};
|
|
2873
|
+
var NPM_VIEW_TIMEOUT_MS = 3e4;
|
|
2874
|
+
var NPM_INSTALL_TIMEOUT_MS = 10 * 6e4;
|
|
2875
|
+
var parseSemver = (version) => {
|
|
2876
|
+
const trimmed = version.trim().replace(/^v/i, "");
|
|
2877
|
+
const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+([0-9A-Za-z.-]+))?$/.exec(trimmed);
|
|
2878
|
+
if (!match) {
|
|
2879
|
+
return void 0;
|
|
2880
|
+
}
|
|
2881
|
+
const major = Number(match[1]);
|
|
2882
|
+
const minor = Number(match[2]);
|
|
2883
|
+
const patch = Number(match[3]);
|
|
2884
|
+
if (!Number.isFinite(major) || !Number.isFinite(minor) || !Number.isFinite(patch)) {
|
|
2885
|
+
return void 0;
|
|
2886
|
+
}
|
|
2887
|
+
const prereleaseRaw = match[4];
|
|
2888
|
+
const prerelease = prereleaseRaw ? prereleaseRaw.split(".").map((identifier) => {
|
|
2889
|
+
const numeric = /^[0-9]+$/.test(identifier) ? Number(identifier) : void 0;
|
|
2890
|
+
return typeof numeric === "number" && Number.isFinite(numeric) ? numeric : identifier;
|
|
2891
|
+
}) : [];
|
|
2892
|
+
return { major, minor, patch, prerelease };
|
|
2893
|
+
};
|
|
2894
|
+
var compareSemverIdentifiers = (a, b) => {
|
|
2895
|
+
if (typeof a === "number" && typeof b === "number") {
|
|
2896
|
+
return a === b ? 0 : a < b ? -1 : 1;
|
|
2897
|
+
}
|
|
2898
|
+
if (typeof a === "number" && typeof b === "string") {
|
|
2899
|
+
return -1;
|
|
2900
|
+
}
|
|
2901
|
+
if (typeof a === "string" && typeof b === "number") {
|
|
2902
|
+
return 1;
|
|
2903
|
+
}
|
|
2904
|
+
const aStr = String(a);
|
|
2905
|
+
const bStr = String(b);
|
|
2906
|
+
if (aStr === bStr) {
|
|
2907
|
+
return 0;
|
|
2908
|
+
}
|
|
2909
|
+
return aStr < bStr ? -1 : 1;
|
|
2910
|
+
};
|
|
2911
|
+
var compareSemver = (a, b) => {
|
|
2912
|
+
if (a.major !== b.major) {
|
|
2913
|
+
return a.major < b.major ? -1 : 1;
|
|
2914
|
+
}
|
|
2915
|
+
if (a.minor !== b.minor) {
|
|
2916
|
+
return a.minor < b.minor ? -1 : 1;
|
|
2917
|
+
}
|
|
2918
|
+
if (a.patch !== b.patch) {
|
|
2919
|
+
return a.patch < b.patch ? -1 : 1;
|
|
2920
|
+
}
|
|
2921
|
+
const aHasPre = a.prerelease.length > 0;
|
|
2922
|
+
const bHasPre = b.prerelease.length > 0;
|
|
2923
|
+
if (!aHasPre && !bHasPre) {
|
|
2924
|
+
return 0;
|
|
2925
|
+
}
|
|
2926
|
+
if (!aHasPre && bHasPre) {
|
|
2927
|
+
return 1;
|
|
2928
|
+
}
|
|
2929
|
+
if (aHasPre && !bHasPre) {
|
|
2930
|
+
return -1;
|
|
2931
|
+
}
|
|
2932
|
+
const length = Math.max(a.prerelease.length, b.prerelease.length);
|
|
2933
|
+
for (let i = 0; i < length; i += 1) {
|
|
2934
|
+
const aId = a.prerelease[i];
|
|
2935
|
+
const bId = b.prerelease[i];
|
|
2936
|
+
if (aId === void 0 && bId === void 0) {
|
|
2937
|
+
return 0;
|
|
2938
|
+
}
|
|
2939
|
+
if (aId === void 0) {
|
|
2940
|
+
return -1;
|
|
2941
|
+
}
|
|
2942
|
+
if (bId === void 0) {
|
|
2943
|
+
return 1;
|
|
2944
|
+
}
|
|
2945
|
+
const cmp = compareSemverIdentifiers(aId, bId);
|
|
2946
|
+
if (cmp !== 0) {
|
|
2947
|
+
return cmp;
|
|
2948
|
+
}
|
|
2949
|
+
}
|
|
2950
|
+
return 0;
|
|
2951
|
+
};
|
|
2952
|
+
var readLatestVersionFromNpm = () => {
|
|
2953
|
+
const npmCommand = getNpmCommand();
|
|
2954
|
+
const result = spawnSync(npmCommand, ["view", "unity-hub-cli", "version"], {
|
|
2955
|
+
encoding: "utf-8",
|
|
2956
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
2957
|
+
timeout: NPM_VIEW_TIMEOUT_MS
|
|
2958
|
+
});
|
|
2959
|
+
if (result.signal) {
|
|
2960
|
+
return { ok: false, reason: "timeout" };
|
|
2961
|
+
}
|
|
2962
|
+
if (result.error) {
|
|
2963
|
+
const errorCode = result.error.code;
|
|
2964
|
+
if (errorCode === "ETIMEDOUT") {
|
|
2965
|
+
return { ok: false, reason: "timeout" };
|
|
2966
|
+
}
|
|
2967
|
+
return { ok: false, reason: "spawn_failed" };
|
|
2968
|
+
}
|
|
2969
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
2970
|
+
return { ok: false, reason: "nonzero_exit" };
|
|
2971
|
+
}
|
|
2972
|
+
const version = result.stdout.trim();
|
|
2973
|
+
if (!version) {
|
|
2974
|
+
return { ok: false, reason: "empty_output" };
|
|
2975
|
+
}
|
|
2976
|
+
return { ok: true, version };
|
|
2977
|
+
};
|
|
2978
|
+
var installLatestVersionGlobally = () => {
|
|
2979
|
+
const npmCommand = getNpmCommand();
|
|
2980
|
+
const result = spawnSync(
|
|
2981
|
+
npmCommand,
|
|
2982
|
+
["install", "-g", "unity-hub-cli@latest", "--ignore-scripts", "--no-fund"],
|
|
2983
|
+
{
|
|
2984
|
+
stdio: "inherit",
|
|
2985
|
+
timeout: NPM_INSTALL_TIMEOUT_MS
|
|
2986
|
+
}
|
|
2987
|
+
);
|
|
2988
|
+
if (result.signal) {
|
|
2989
|
+
return { ok: false, reason: "timeout" };
|
|
2990
|
+
}
|
|
2991
|
+
if (result.error) {
|
|
2992
|
+
const errorCode = result.error.code;
|
|
2993
|
+
if (errorCode === "ETIMEDOUT") {
|
|
2994
|
+
return { ok: false, reason: "timeout" };
|
|
2995
|
+
}
|
|
2996
|
+
return { ok: false, reason: "spawn_failed" };
|
|
2997
|
+
}
|
|
2998
|
+
if (typeof result.status === "number" && result.status !== 0) {
|
|
2999
|
+
return { ok: false, reason: "nonzero_exit" };
|
|
3000
|
+
}
|
|
3001
|
+
return { ok: true };
|
|
3002
|
+
};
|
|
2746
3003
|
var getShellInitScript = () => {
|
|
2747
3004
|
const shell = process3.env["SHELL"] ?? "";
|
|
2748
3005
|
const isWindows = process3.platform === "win32";
|
|
@@ -2793,6 +3050,73 @@ end`;
|
|
|
2793
3050
|
};
|
|
2794
3051
|
var bootstrap = async () => {
|
|
2795
3052
|
const args = process3.argv.slice(2);
|
|
3053
|
+
if (args.includes("-v") || args.includes("--version")) {
|
|
3054
|
+
console.log(getVersion());
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
if (args[0] === "update") {
|
|
3058
|
+
const currentVersion = getVersion();
|
|
3059
|
+
const latestVersionResult = readLatestVersionFromNpm();
|
|
3060
|
+
if (!latestVersionResult.ok) {
|
|
3061
|
+
if (latestVersionResult.reason === "timeout") {
|
|
3062
|
+
console.error("Failed to read the latest version from npm (timeout). Please check your network/proxy settings and try again.");
|
|
3063
|
+
} else if (latestVersionResult.reason === "spawn_failed") {
|
|
3064
|
+
console.error("Failed to run npm. Please ensure npm is installed and available in your PATH.");
|
|
3065
|
+
} else {
|
|
3066
|
+
console.error("Failed to read the latest version from npm. Please ensure you can access the npm registry.");
|
|
3067
|
+
}
|
|
3068
|
+
process3.exitCode = 1;
|
|
3069
|
+
return;
|
|
3070
|
+
}
|
|
3071
|
+
const latestVersion = latestVersionResult.version;
|
|
3072
|
+
const parsedCurrent = parseSemver(currentVersion);
|
|
3073
|
+
const parsedLatest = parseSemver(latestVersion);
|
|
3074
|
+
if (parsedCurrent && parsedLatest) {
|
|
3075
|
+
const cmp = compareSemver(parsedLatest, parsedCurrent);
|
|
3076
|
+
if (cmp <= 0) {
|
|
3077
|
+
console.log(`Already up to date: ${currentVersion}`);
|
|
3078
|
+
if (cmp < 0) {
|
|
3079
|
+
console.log(`Current version is newer than npm latest: ${latestVersion}`);
|
|
3080
|
+
}
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
3083
|
+
} else if (latestVersion === currentVersion) {
|
|
3084
|
+
console.log(`Already up to date: ${currentVersion}`);
|
|
3085
|
+
return;
|
|
3086
|
+
}
|
|
3087
|
+
console.log(`Current version: ${currentVersion}`);
|
|
3088
|
+
console.log(`Latest version: ${latestVersion}`);
|
|
3089
|
+
console.log("This will update the global installation via npm: npm install -g unity-hub-cli@latest --ignore-scripts --no-fund");
|
|
3090
|
+
console.log("");
|
|
3091
|
+
if (!process3.stdin.isTTY) {
|
|
3092
|
+
console.error("Interactive confirmation is not available in this environment (stdin is not a TTY).");
|
|
3093
|
+
process3.exitCode = 1;
|
|
3094
|
+
return;
|
|
3095
|
+
}
|
|
3096
|
+
if (!parsedCurrent || !parsedLatest) {
|
|
3097
|
+
console.log("Warning: could not compare versions reliably; proceeding may downgrade your installation.");
|
|
3098
|
+
console.log("");
|
|
3099
|
+
}
|
|
3100
|
+
const confirmed = await askConfirmation("Proceed with update? (y/n): ");
|
|
3101
|
+
if (!confirmed) {
|
|
3102
|
+
console.log("Update cancelled.");
|
|
3103
|
+
return;
|
|
3104
|
+
}
|
|
3105
|
+
const installResult = installLatestVersionGlobally();
|
|
3106
|
+
if (!installResult.ok) {
|
|
3107
|
+
if (installResult.reason === "timeout") {
|
|
3108
|
+
console.error("Update failed (timeout). Please check your network/proxy settings and try again.");
|
|
3109
|
+
} else if (installResult.reason === "spawn_failed") {
|
|
3110
|
+
console.error("Failed to run npm. Please ensure npm is installed and available in your PATH.");
|
|
3111
|
+
} else {
|
|
3112
|
+
console.error("Update failed. Please check the npm output above.");
|
|
3113
|
+
}
|
|
3114
|
+
process3.exitCode = 1;
|
|
3115
|
+
return;
|
|
3116
|
+
}
|
|
3117
|
+
console.log(`Updated to: ${latestVersion}`);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
2796
3120
|
if (args.includes("--shell-init")) {
|
|
2797
3121
|
const isDryRun = args.includes("--dry-run");
|
|
2798
3122
|
if (isDryRun) {
|
|
@@ -2809,6 +3133,11 @@ var bootstrap = async () => {
|
|
|
2809
3133
|
`);
|
|
2810
3134
|
previewShellInit();
|
|
2811
3135
|
console.log("");
|
|
3136
|
+
if (!process3.stdin.isTTY) {
|
|
3137
|
+
console.error("Interactive confirmation is not available in this environment (stdin is not a TTY).");
|
|
3138
|
+
process3.exitCode = 1;
|
|
3139
|
+
return;
|
|
3140
|
+
}
|
|
2812
3141
|
const confirmed = await askConfirmation("Proceed with installation? (y/n): ");
|
|
2813
3142
|
if (!confirmed) {
|
|
2814
3143
|
console.log("Installation cancelled.");
|
|
@@ -2896,6 +3225,7 @@ var bootstrap = async () => {
|
|
|
2896
3225
|
onLaunchEditorOnly: (project) => launchEditorOnlyUseCase.execute(project),
|
|
2897
3226
|
onTerminate: (project) => terminateProjectUseCase.execute(project),
|
|
2898
3227
|
onRefresh: () => listProjectsUseCase.execute(),
|
|
3228
|
+
onToggleFavorite: (project) => unityHubReader.toggleFavorite(project.path),
|
|
2899
3229
|
useGitRootName,
|
|
2900
3230
|
outputPathOnExit,
|
|
2901
3231
|
onSetExitPath: (path) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unity-hub-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.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": {
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"eslint-config-prettier": "10.1.8",
|
|
65
65
|
"eslint-import-resolver-typescript": "4.4.4",
|
|
66
66
|
"eslint-plugin-import": "2.32.0",
|
|
67
|
-
"prettier": "3.7.
|
|
67
|
+
"prettier": "3.7.4",
|
|
68
68
|
"tsup": "8.5.1",
|
|
69
69
|
"tsx": "4.21.0",
|
|
70
70
|
"typescript": "5.9.3",
|