unity-hub-cli 0.11.0 → 0.13.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 +13 -2
- package/dist/index.js +396 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,9 +8,12 @@ A CLI tool that displays the same content as Unity Hub in an Ink-based TUI, allo
|
|
|
8
8
|
|
|
9
9
|
## Requirements
|
|
10
10
|
|
|
11
|
-
- macOS
|
|
11
|
+
- macOS or Windows 10/11
|
|
12
12
|
- Node.js 20+
|
|
13
|
-
- Unity Hub
|
|
13
|
+
- Unity Hub
|
|
14
|
+
- macOS: `~/Library/Application Support/UnityHub/projects-v1.json`
|
|
15
|
+
- Windows: `%APPDATA%\UnityHub\projects-v1.json`
|
|
16
|
+
- Windows Editor path (default): `C:\\Program Files\\Unity\\Hub\\Editor\\<version>\\Editor\\Unity.exe`
|
|
14
17
|
|
|
15
18
|
## Usage
|
|
16
19
|
|
|
@@ -37,6 +40,14 @@ npx unity-hub-cli
|
|
|
37
40
|
node dist/index.js
|
|
38
41
|
```
|
|
39
42
|
|
|
43
|
+
On Windows, it works from PowerShell and CMD. Git Bash is supported when running inside a ConPTY-based terminal (Windows Terminal or VS Code/Cursor integrated terminal). On standalone Git Bash (MinTTY), raw mode is not supported; use PowerShell/CMD/Windows Terminal. If you must use MinTTY Git Bash, run one of the following:
|
|
44
|
+
|
|
45
|
+
- `winpty cmd.exe /c npx unity-hub-cli`
|
|
46
|
+
- `winpty powershell.exe -NoProfile -Command npx unity-hub-cli`
|
|
47
|
+
- If already built: `npm run build && winpty node dist/index.js`
|
|
48
|
+
|
|
49
|
+
See `https://github.com/vadimdemedes/ink/#israwmodesupported`.
|
|
50
|
+
|
|
40
51
|
By default, the project list uses the Git repository root folder name when available.
|
|
41
52
|
|
|
42
53
|
### CLI Options
|
package/dist/index.js
CHANGED
|
@@ -124,9 +124,43 @@ var MacEditorPathResolver = class {
|
|
|
124
124
|
}
|
|
125
125
|
};
|
|
126
126
|
|
|
127
|
+
// src/infrastructure/editor.win.ts
|
|
128
|
+
import { constants as constants2 } from "fs";
|
|
129
|
+
import { access as access2 } from "fs/promises";
|
|
130
|
+
import { join as join2 } from "path";
|
|
131
|
+
var buildCandidateBases = () => {
|
|
132
|
+
const candidates = [];
|
|
133
|
+
const programFiles = process.env.PROGRAMFILES ?? "C:\\Program Files";
|
|
134
|
+
const programW6432 = process.env.ProgramW6432 ?? process.env.PROGRAMFILES;
|
|
135
|
+
const localAppData = process.env.LOCALAPPDATA;
|
|
136
|
+
candidates.push(join2(programFiles, "Unity", "Hub", "Editor"));
|
|
137
|
+
if (programW6432) {
|
|
138
|
+
candidates.push(join2(programW6432, "Unity", "Hub", "Editor"));
|
|
139
|
+
}
|
|
140
|
+
if (localAppData) {
|
|
141
|
+
candidates.push(join2(localAppData, "Unity", "Hub", "Editor"));
|
|
142
|
+
}
|
|
143
|
+
return Array.from(new Set(candidates));
|
|
144
|
+
};
|
|
145
|
+
var WinEditorPathResolver = class {
|
|
146
|
+
async resolve(version) {
|
|
147
|
+
const tried = [];
|
|
148
|
+
for (const base of buildCandidateBases()) {
|
|
149
|
+
const candidate = join2(base, version.value, "Editor", "Unity.exe");
|
|
150
|
+
try {
|
|
151
|
+
await access2(candidate, constants2.F_OK);
|
|
152
|
+
return candidate;
|
|
153
|
+
} catch {
|
|
154
|
+
tried.push(candidate);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
throw new Error(`Unity Editor not found for version ${version.value}. Tried: ${tried.join(" , ")}`);
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
127
161
|
// src/infrastructure/git.ts
|
|
128
162
|
import { readFile, stat } from "fs/promises";
|
|
129
|
-
import { dirname, join as
|
|
163
|
+
import { dirname, join as join3, resolve } from "path";
|
|
130
164
|
var HEAD_FILE = "HEAD";
|
|
131
165
|
var GIT_DIR = ".git";
|
|
132
166
|
var MAX_ASCENT = 50;
|
|
@@ -149,7 +183,7 @@ var isFile = async (path) => {
|
|
|
149
183
|
var findGitDir = async (start) => {
|
|
150
184
|
let current = resolve(start);
|
|
151
185
|
for (let depth = 0; depth < MAX_ASCENT; depth += 1) {
|
|
152
|
-
const candidate =
|
|
186
|
+
const candidate = join3(current, GIT_DIR);
|
|
153
187
|
if (await isDirectory(candidate)) {
|
|
154
188
|
return candidate;
|
|
155
189
|
}
|
|
@@ -193,7 +227,7 @@ var GitRepositoryInfoReader = class {
|
|
|
193
227
|
return void 0;
|
|
194
228
|
}
|
|
195
229
|
try {
|
|
196
|
-
const headPath =
|
|
230
|
+
const headPath = join3(gitDir, HEAD_FILE);
|
|
197
231
|
const content = await readFile(headPath, "utf8");
|
|
198
232
|
const branch = parseHead(content);
|
|
199
233
|
const root = dirname(gitDir);
|
|
@@ -209,7 +243,7 @@ import { spawn } from "child_process";
|
|
|
209
243
|
var NodeProcessLauncher = class {
|
|
210
244
|
async launch(command, args, options) {
|
|
211
245
|
const detached = options?.detached ?? false;
|
|
212
|
-
await new Promise((
|
|
246
|
+
await new Promise((resolve4, reject) => {
|
|
213
247
|
const child = spawn(command, args, {
|
|
214
248
|
detached,
|
|
215
249
|
stdio: "ignore"
|
|
@@ -221,7 +255,7 @@ var NodeProcessLauncher = class {
|
|
|
221
255
|
const handleSpawn = () => {
|
|
222
256
|
child.off("error", handleError);
|
|
223
257
|
child.unref();
|
|
224
|
-
|
|
258
|
+
resolve4();
|
|
225
259
|
};
|
|
226
260
|
child.once("error", handleError);
|
|
227
261
|
child.once("spawn", handleSpawn);
|
|
@@ -275,7 +309,7 @@ var sortByFavoriteThenLastModified = (projects) => {
|
|
|
275
309
|
return timeB - timeA;
|
|
276
310
|
});
|
|
277
311
|
};
|
|
278
|
-
var
|
|
312
|
+
var MacUnityHubProjectsReader = class {
|
|
279
313
|
async listProjects() {
|
|
280
314
|
let content;
|
|
281
315
|
try {
|
|
@@ -359,11 +393,142 @@ var UnityHubProjectsReader = class {
|
|
|
359
393
|
}
|
|
360
394
|
};
|
|
361
395
|
|
|
396
|
+
// src/infrastructure/unityhub.win.ts
|
|
397
|
+
import { readFile as readFile3, writeFile as writeFile2 } from "fs/promises";
|
|
398
|
+
import { basename as basename2, join as join4 } from "path";
|
|
399
|
+
var HUB_DIR = join4(process.env.APPDATA ?? "", "UnityHub");
|
|
400
|
+
var HUB_PROJECTS_PATH2 = join4(HUB_DIR, "projects-v1.json");
|
|
401
|
+
var schemaVersion2 = "v1";
|
|
402
|
+
var toUnityProject2 = (entry) => {
|
|
403
|
+
const safePath = entry.path;
|
|
404
|
+
if (!safePath) {
|
|
405
|
+
throw new Error("Unity Hub entry is missing project path");
|
|
406
|
+
}
|
|
407
|
+
const version = entry.version;
|
|
408
|
+
if (!version) {
|
|
409
|
+
throw new Error(`Unity Hub entry ${safePath} is missing version`);
|
|
410
|
+
}
|
|
411
|
+
const lastModified = typeof entry.lastModified === "number" ? new Date(entry.lastModified) : void 0;
|
|
412
|
+
return {
|
|
413
|
+
id: safePath,
|
|
414
|
+
title: entry.title?.trim() || basename2(safePath),
|
|
415
|
+
path: safePath,
|
|
416
|
+
version: { value: version },
|
|
417
|
+
lastModified,
|
|
418
|
+
favorite: entry.isFavorite === true
|
|
419
|
+
};
|
|
420
|
+
};
|
|
421
|
+
var normalizeValue2 = (value) => value.toLocaleLowerCase();
|
|
422
|
+
var sortByFavoriteThenLastModified2 = (projects) => {
|
|
423
|
+
return [...projects].sort((a, b) => {
|
|
424
|
+
const favoriteRankA = a.favorite ? 0 : 1;
|
|
425
|
+
const favoriteRankB = b.favorite ? 0 : 1;
|
|
426
|
+
if (favoriteRankA !== favoriteRankB) {
|
|
427
|
+
return favoriteRankA - favoriteRankB;
|
|
428
|
+
}
|
|
429
|
+
const fallbackTime = 0;
|
|
430
|
+
const timeA = a.lastModified?.getTime() ?? fallbackTime;
|
|
431
|
+
const timeB = b.lastModified?.getTime() ?? fallbackTime;
|
|
432
|
+
if (timeA === timeB) {
|
|
433
|
+
const titleA = normalizeValue2(a.title);
|
|
434
|
+
const titleB = normalizeValue2(b.title);
|
|
435
|
+
if (titleA === titleB) {
|
|
436
|
+
return normalizeValue2(a.path).localeCompare(normalizeValue2(b.path));
|
|
437
|
+
}
|
|
438
|
+
return titleA.localeCompare(titleB);
|
|
439
|
+
}
|
|
440
|
+
return timeB - timeA;
|
|
441
|
+
});
|
|
442
|
+
};
|
|
443
|
+
var WinUnityHubProjectsReader = class {
|
|
444
|
+
async listProjects() {
|
|
445
|
+
let content;
|
|
446
|
+
try {
|
|
447
|
+
content = await readFile3(HUB_PROJECTS_PATH2, "utf8");
|
|
448
|
+
} catch {
|
|
449
|
+
throw new Error(
|
|
450
|
+
`Unity Hub project list not found (${HUB_PROJECTS_PATH2}).`
|
|
451
|
+
);
|
|
452
|
+
}
|
|
453
|
+
let json;
|
|
454
|
+
try {
|
|
455
|
+
json = JSON.parse(content);
|
|
456
|
+
} catch {
|
|
457
|
+
throw new Error("Unable to read the Unity Hub project list (permissions/format error).");
|
|
458
|
+
}
|
|
459
|
+
if (json.schema_version && json.schema_version !== schemaVersion2) {
|
|
460
|
+
throw new Error(`Unsupported schema_version (${json.schema_version}).`);
|
|
461
|
+
}
|
|
462
|
+
const entries = Object.values(json.data ?? {});
|
|
463
|
+
if (entries.length === 0) {
|
|
464
|
+
return [];
|
|
465
|
+
}
|
|
466
|
+
const projects = entries.map(toUnityProject2);
|
|
467
|
+
return sortByFavoriteThenLastModified2(projects);
|
|
468
|
+
}
|
|
469
|
+
async updateLastModified(projectPath, date) {
|
|
470
|
+
let content;
|
|
471
|
+
try {
|
|
472
|
+
content = await readFile3(HUB_PROJECTS_PATH2, "utf8");
|
|
473
|
+
} catch {
|
|
474
|
+
throw new Error(
|
|
475
|
+
`Unity Hub project list not found (${HUB_PROJECTS_PATH2}).`
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
let json;
|
|
479
|
+
try {
|
|
480
|
+
json = JSON.parse(content);
|
|
481
|
+
} catch {
|
|
482
|
+
throw new Error("Unable to read the Unity Hub project list (permissions/format error).");
|
|
483
|
+
}
|
|
484
|
+
if (!json.data) {
|
|
485
|
+
return;
|
|
486
|
+
}
|
|
487
|
+
const projectKey = Object.keys(json.data).find((key) => json.data?.[key]?.path === projectPath);
|
|
488
|
+
if (!projectKey) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const original = json.data[projectKey];
|
|
492
|
+
if (!original) {
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
json.data[projectKey] = {
|
|
496
|
+
...original,
|
|
497
|
+
lastModified: date.getTime()
|
|
498
|
+
};
|
|
499
|
+
await writeFile2(HUB_PROJECTS_PATH2, JSON.stringify(json, void 0, 2), "utf8");
|
|
500
|
+
}
|
|
501
|
+
async readCliArgs(projectPath) {
|
|
502
|
+
const infoPath = join4(HUB_DIR, "projectsInfo.json");
|
|
503
|
+
let content;
|
|
504
|
+
try {
|
|
505
|
+
content = await readFile3(infoPath, "utf8");
|
|
506
|
+
} catch {
|
|
507
|
+
return [];
|
|
508
|
+
}
|
|
509
|
+
let json;
|
|
510
|
+
try {
|
|
511
|
+
json = JSON.parse(content);
|
|
512
|
+
} catch {
|
|
513
|
+
return [];
|
|
514
|
+
}
|
|
515
|
+
const entry = json[projectPath];
|
|
516
|
+
if (!entry?.cliArgs) {
|
|
517
|
+
return [];
|
|
518
|
+
}
|
|
519
|
+
const tokens = entry.cliArgs.match(/(?:"[^"]*"|'[^']*'|[^\s"']+)/g);
|
|
520
|
+
if (!tokens) {
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
return tokens.map((token) => token.replace(/^['"]|['"]$/g, ""));
|
|
524
|
+
}
|
|
525
|
+
};
|
|
526
|
+
|
|
362
527
|
// src/infrastructure/unityLock.ts
|
|
363
528
|
import { execFile } from "child_process";
|
|
364
|
-
import { constants as
|
|
365
|
-
import { access as
|
|
366
|
-
import { join as
|
|
529
|
+
import { constants as constants3 } from "fs";
|
|
530
|
+
import { access as access3, rm } from "fs/promises";
|
|
531
|
+
import { join as join5 } from "path";
|
|
367
532
|
import { promisify } from "util";
|
|
368
533
|
var execFileAsync = promisify(execFile);
|
|
369
534
|
var buildBringToFrontScript = (pid) => {
|
|
@@ -371,7 +536,7 @@ var buildBringToFrontScript = (pid) => {
|
|
|
371
536
|
};
|
|
372
537
|
var pathExists = async (target) => {
|
|
373
538
|
try {
|
|
374
|
-
await
|
|
539
|
+
await access3(target, constants3.F_OK);
|
|
375
540
|
return true;
|
|
376
541
|
} catch {
|
|
377
542
|
return false;
|
|
@@ -391,7 +556,7 @@ var UnityLockChecker = class {
|
|
|
391
556
|
await this.bringUnityToFront(activeProcess.pid);
|
|
392
557
|
return "skip";
|
|
393
558
|
}
|
|
394
|
-
const lockfilePath =
|
|
559
|
+
const lockfilePath = join5(projectPath, "Temp", "UnityLockfile");
|
|
395
560
|
const hasLockfile = await pathExists(lockfilePath);
|
|
396
561
|
if (!hasLockfile) {
|
|
397
562
|
return "allow";
|
|
@@ -429,7 +594,7 @@ var UnityLockChecker = class {
|
|
|
429
594
|
};
|
|
430
595
|
var UnityLockStatusReader = class {
|
|
431
596
|
async isLocked(projectPath) {
|
|
432
|
-
const lockfilePath =
|
|
597
|
+
const lockfilePath = join5(projectPath, "Temp", "UnityLockfile");
|
|
433
598
|
return await pathExists(lockfilePath);
|
|
434
599
|
}
|
|
435
600
|
};
|
|
@@ -602,13 +767,187 @@ var MacUnityProcessTerminator = class {
|
|
|
602
767
|
}
|
|
603
768
|
};
|
|
604
769
|
|
|
770
|
+
// src/infrastructure/unityProcess.win.ts
|
|
771
|
+
import { execFile as execFile3 } from "child_process";
|
|
772
|
+
import { resolve as resolve3 } from "path";
|
|
773
|
+
import { promisify as promisify3 } from "util";
|
|
774
|
+
var execFileAsync3 = promisify3(execFile3);
|
|
775
|
+
var PROJECT_PATH_PATTERN2 = /-(?:projectPath|projectpath)(?:=|\s+)("[^"]+"|'[^']+'|[^\s"']+)/i;
|
|
776
|
+
var TERMINATE_TIMEOUT_MILLIS2 = 5e3;
|
|
777
|
+
var TERMINATE_POLL_INTERVAL_MILLIS2 = 200;
|
|
778
|
+
var delay2 = async (duration) => {
|
|
779
|
+
await new Promise((resolveDelay) => {
|
|
780
|
+
setTimeout(() => {
|
|
781
|
+
resolveDelay();
|
|
782
|
+
}, duration);
|
|
783
|
+
});
|
|
784
|
+
};
|
|
785
|
+
var normalizePath2 = (target) => {
|
|
786
|
+
const resolved = resolve3(target);
|
|
787
|
+
if (resolved.endsWith("/") || resolved.endsWith("\\")) {
|
|
788
|
+
return resolved.slice(0, -1);
|
|
789
|
+
}
|
|
790
|
+
return resolved;
|
|
791
|
+
};
|
|
792
|
+
var arePathsEqual2 = (left, right) => {
|
|
793
|
+
const normalizedLeft = normalizePath2(left);
|
|
794
|
+
const normalizedRight = normalizePath2(right);
|
|
795
|
+
return normalizedLeft.localeCompare(normalizedRight, void 0, { sensitivity: "base" }) === 0;
|
|
796
|
+
};
|
|
797
|
+
var extractProjectPath2 = (command) => {
|
|
798
|
+
const match = command.match(PROJECT_PATH_PATTERN2);
|
|
799
|
+
if (!match) {
|
|
800
|
+
return void 0;
|
|
801
|
+
}
|
|
802
|
+
const raw = match[1];
|
|
803
|
+
if (!raw) {
|
|
804
|
+
return void 0;
|
|
805
|
+
}
|
|
806
|
+
const trimmed = raw.trim();
|
|
807
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"')) {
|
|
808
|
+
return trimmed.slice(1, -1);
|
|
809
|
+
}
|
|
810
|
+
if (trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
811
|
+
return trimmed.slice(1, -1);
|
|
812
|
+
}
|
|
813
|
+
return trimmed;
|
|
814
|
+
};
|
|
815
|
+
var isProcessMissingError2 = (error) => {
|
|
816
|
+
if (typeof error !== "object" || error === null) {
|
|
817
|
+
return false;
|
|
818
|
+
}
|
|
819
|
+
const nodeError = error;
|
|
820
|
+
return nodeError.code === "ESRCH";
|
|
821
|
+
};
|
|
822
|
+
var ensureProcessAlive2 = (pid) => {
|
|
823
|
+
try {
|
|
824
|
+
process.kill(pid, 0);
|
|
825
|
+
return true;
|
|
826
|
+
} catch (error) {
|
|
827
|
+
if (isProcessMissingError2(error)) {
|
|
828
|
+
return false;
|
|
829
|
+
}
|
|
830
|
+
return false;
|
|
831
|
+
}
|
|
832
|
+
};
|
|
833
|
+
var parsePowershellJson = (jsonText) => {
|
|
834
|
+
try {
|
|
835
|
+
const parsed = JSON.parse(jsonText);
|
|
836
|
+
if (!parsed) {
|
|
837
|
+
return [];
|
|
838
|
+
}
|
|
839
|
+
if (Array.isArray(parsed)) {
|
|
840
|
+
return parsed.filter((r) => r && typeof r === "object" && typeof r.ProcessId === "number");
|
|
841
|
+
}
|
|
842
|
+
const single = parsed;
|
|
843
|
+
if (typeof single.ProcessId === "number") {
|
|
844
|
+
return [single];
|
|
845
|
+
}
|
|
846
|
+
return [];
|
|
847
|
+
} catch {
|
|
848
|
+
return [];
|
|
849
|
+
}
|
|
850
|
+
};
|
|
851
|
+
var WinUnityProcessReader = class {
|
|
852
|
+
async findByProjectPath(projectPath) {
|
|
853
|
+
const normalizedTarget = normalizePath2(projectPath);
|
|
854
|
+
const processes = await this.listUnityProcesses();
|
|
855
|
+
return processes.find((candidate) => arePathsEqual2(candidate.projectPath, normalizedTarget));
|
|
856
|
+
}
|
|
857
|
+
async listUnityProcesses() {
|
|
858
|
+
const psCommand = [
|
|
859
|
+
"$ErrorActionPreference = 'SilentlyContinue';",
|
|
860
|
+
`$procs = Get-CimInstance Win32_Process -Filter "Name='Unity.exe'";`,
|
|
861
|
+
"$procs | Select-Object ProcessId, CommandLine | ConvertTo-Json -Compress"
|
|
862
|
+
].join(" ");
|
|
863
|
+
let stdout;
|
|
864
|
+
try {
|
|
865
|
+
const result = await execFileAsync3(
|
|
866
|
+
"powershell.exe",
|
|
867
|
+
[
|
|
868
|
+
"-NoProfile",
|
|
869
|
+
"-NonInteractive",
|
|
870
|
+
"-ExecutionPolicy",
|
|
871
|
+
"Bypass",
|
|
872
|
+
"-Command",
|
|
873
|
+
psCommand
|
|
874
|
+
],
|
|
875
|
+
{ encoding: "utf8" }
|
|
876
|
+
);
|
|
877
|
+
stdout = (result.stdout ?? "").trim();
|
|
878
|
+
} catch (error) {
|
|
879
|
+
throw new Error(`Failed to retrieve Unity process list: ${error instanceof Error ? error.message : String(error)}`);
|
|
880
|
+
}
|
|
881
|
+
const rows = parsePowershellJson(stdout);
|
|
882
|
+
return rows.map((row) => {
|
|
883
|
+
const pidValue = row.ProcessId;
|
|
884
|
+
if (!Number.isFinite(pidValue)) {
|
|
885
|
+
return void 0;
|
|
886
|
+
}
|
|
887
|
+
const commandLine = row.CommandLine ?? "";
|
|
888
|
+
const projectArgument = extractProjectPath2(commandLine);
|
|
889
|
+
if (!projectArgument) {
|
|
890
|
+
return void 0;
|
|
891
|
+
}
|
|
892
|
+
return {
|
|
893
|
+
pid: pidValue,
|
|
894
|
+
projectPath: normalizePath2(projectArgument)
|
|
895
|
+
};
|
|
896
|
+
}).filter((p) => Boolean(p));
|
|
897
|
+
}
|
|
898
|
+
};
|
|
899
|
+
var WinUnityProcessTerminator = class {
|
|
900
|
+
async terminate(unityProcess) {
|
|
901
|
+
try {
|
|
902
|
+
await execFileAsync3("powershell.exe", [
|
|
903
|
+
"-NoProfile",
|
|
904
|
+
"-NonInteractive",
|
|
905
|
+
"-ExecutionPolicy",
|
|
906
|
+
"Bypass",
|
|
907
|
+
"-Command",
|
|
908
|
+
`Stop-Process -Id ${unityProcess.pid}`
|
|
909
|
+
]);
|
|
910
|
+
} catch (error) {
|
|
911
|
+
if (!ensureProcessAlive2(unityProcess.pid)) {
|
|
912
|
+
return { terminated: true, stage: "sigterm" };
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
const deadline = Date.now() + TERMINATE_TIMEOUT_MILLIS2;
|
|
916
|
+
while (Date.now() < deadline) {
|
|
917
|
+
await delay2(TERMINATE_POLL_INTERVAL_MILLIS2);
|
|
918
|
+
const alive = ensureProcessAlive2(unityProcess.pid);
|
|
919
|
+
if (!alive) {
|
|
920
|
+
return { terminated: true, stage: "sigterm" };
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
try {
|
|
924
|
+
await execFileAsync3("powershell.exe", [
|
|
925
|
+
"-NoProfile",
|
|
926
|
+
"-NonInteractive",
|
|
927
|
+
"-ExecutionPolicy",
|
|
928
|
+
"Bypass",
|
|
929
|
+
"-Command",
|
|
930
|
+
`Stop-Process -Id ${unityProcess.pid} -Force`
|
|
931
|
+
]);
|
|
932
|
+
} catch (error) {
|
|
933
|
+
if (!ensureProcessAlive2(unityProcess.pid)) {
|
|
934
|
+
return { terminated: true, stage: "sigkill" };
|
|
935
|
+
}
|
|
936
|
+
return { terminated: false };
|
|
937
|
+
}
|
|
938
|
+
await delay2(TERMINATE_POLL_INTERVAL_MILLIS2);
|
|
939
|
+
const aliveAfterKill = ensureProcessAlive2(unityProcess.pid);
|
|
940
|
+
return aliveAfterKill ? { terminated: false } : { terminated: true, stage: "sigkill" };
|
|
941
|
+
}
|
|
942
|
+
};
|
|
943
|
+
|
|
605
944
|
// src/infrastructure/unityTemp.ts
|
|
606
945
|
import { rm as rm2 } from "fs/promises";
|
|
607
|
-
import { join as
|
|
946
|
+
import { join as join6 } from "path";
|
|
608
947
|
var TEMP_DIRECTORY_NAME = "Temp";
|
|
609
948
|
var UnityTempDirectoryCleaner = class {
|
|
610
949
|
async clean(projectPath) {
|
|
611
|
-
const tempDirectoryPath =
|
|
950
|
+
const tempDirectoryPath = join6(projectPath, TEMP_DIRECTORY_NAME);
|
|
612
951
|
try {
|
|
613
952
|
await rm2(tempDirectoryPath, {
|
|
614
953
|
recursive: true,
|
|
@@ -685,6 +1024,13 @@ var shortenHomePath = (targetPath) => {
|
|
|
685
1024
|
};
|
|
686
1025
|
var buildCdCommand = (targetPath) => {
|
|
687
1026
|
if (process.platform === "win32") {
|
|
1027
|
+
const isGitBash = Boolean(process.env.MSYSTEM) || /bash/i.test(process.env.SHELL ?? "");
|
|
1028
|
+
if (isGitBash) {
|
|
1029
|
+
const windowsPath = targetPath;
|
|
1030
|
+
const msysPath = windowsPath.replace(/^([A-Za-z]):[\\/]/, (_, drive) => `/${drive.toLowerCase()}/`).replace(/\\/g, "/");
|
|
1031
|
+
const escapedForPosix2 = msysPath.replace(/'/g, "'\\''");
|
|
1032
|
+
return `cd '${escapedForPosix2}'`;
|
|
1033
|
+
}
|
|
688
1034
|
const escapedForWindows = targetPath.replace(/"/g, '""');
|
|
689
1035
|
return `cd "${escapedForWindows}"`;
|
|
690
1036
|
}
|
|
@@ -1015,7 +1361,7 @@ var VisibilityPanel = ({ visibility, focusedIndex, width }) => {
|
|
|
1015
1361
|
import { useEffect, useState } from "react";
|
|
1016
1362
|
|
|
1017
1363
|
// src/infrastructure/config.ts
|
|
1018
|
-
import { mkdir, readFile as
|
|
1364
|
+
import { mkdir, readFile as readFile4, writeFile as writeFile3 } from "fs/promises";
|
|
1019
1365
|
var defaultSortPreferences = {
|
|
1020
1366
|
favoritesFirst: true,
|
|
1021
1367
|
primary: "updated",
|
|
@@ -1030,6 +1376,10 @@ var defaultAppConfig = {
|
|
|
1030
1376
|
visibility: defaultVisibilityPreferences
|
|
1031
1377
|
};
|
|
1032
1378
|
var getConfigDir = () => {
|
|
1379
|
+
if (process.platform === "win32") {
|
|
1380
|
+
const appdata = process.env.APPDATA ?? "";
|
|
1381
|
+
return appdata ? `${appdata}\\UnityHubCli` : "UnityHubCli";
|
|
1382
|
+
}
|
|
1033
1383
|
const home = process.env.HOME ?? "";
|
|
1034
1384
|
return `${home}/Library/Application Support/UnityHubCli`;
|
|
1035
1385
|
};
|
|
@@ -1066,7 +1416,7 @@ var sanitizeAppConfig = (input) => {
|
|
|
1066
1416
|
};
|
|
1067
1417
|
var readAppConfig = async () => {
|
|
1068
1418
|
try {
|
|
1069
|
-
const content = await
|
|
1419
|
+
const content = await readFile4(getConfigPath(), "utf8");
|
|
1070
1420
|
const json = JSON.parse(content);
|
|
1071
1421
|
return sanitizeAppConfig(json);
|
|
1072
1422
|
} catch {
|
|
@@ -1079,7 +1429,7 @@ var writeAppConfig = async (config) => {
|
|
|
1079
1429
|
} catch {
|
|
1080
1430
|
}
|
|
1081
1431
|
const json = JSON.stringify(sanitizeAppConfig(config), void 0, 2);
|
|
1082
|
-
await
|
|
1432
|
+
await writeFile3(getConfigPath(), json, "utf8");
|
|
1083
1433
|
};
|
|
1084
1434
|
var readSortPreferences = async () => {
|
|
1085
1435
|
const config = await readAppConfig();
|
|
@@ -1224,6 +1574,9 @@ var App = ({
|
|
|
1224
1574
|
const [isRefreshing, setIsRefreshing] = useState4(false);
|
|
1225
1575
|
const [sortMenuIndex, setSortMenuIndex] = useState4(0);
|
|
1226
1576
|
const { sortPreferences, setSortPreferences } = useSortPreferences();
|
|
1577
|
+
const clearScreen = useCallback(() => {
|
|
1578
|
+
stdout?.write("\x1B[2J\x1B[3J\x1B[H");
|
|
1579
|
+
}, [stdout]);
|
|
1227
1580
|
const sortedProjects = useMemo2(() => {
|
|
1228
1581
|
const fallbackTime = 0;
|
|
1229
1582
|
const getNameKey = (view) => {
|
|
@@ -1533,6 +1886,7 @@ var App = ({
|
|
|
1533
1886
|
useInput((input, key) => {
|
|
1534
1887
|
if (isSortMenuOpen) {
|
|
1535
1888
|
if (key.escape || input === "\x1B") {
|
|
1889
|
+
clearScreen();
|
|
1536
1890
|
setIsSortMenuOpen(false);
|
|
1537
1891
|
return;
|
|
1538
1892
|
}
|
|
@@ -1570,6 +1924,7 @@ var App = ({
|
|
|
1570
1924
|
}
|
|
1571
1925
|
if (isVisibilityMenuOpen) {
|
|
1572
1926
|
if (key.escape || input === "\x1B") {
|
|
1927
|
+
clearScreen();
|
|
1573
1928
|
setIsVisibilityMenuOpen(false);
|
|
1574
1929
|
return;
|
|
1575
1930
|
}
|
|
@@ -1602,12 +1957,14 @@ var App = ({
|
|
|
1602
1957
|
return;
|
|
1603
1958
|
}
|
|
1604
1959
|
if (input === "S" || input === "s") {
|
|
1960
|
+
clearScreen();
|
|
1605
1961
|
setIsVisibilityMenuOpen(false);
|
|
1606
1962
|
setIsSortMenuOpen(true);
|
|
1607
1963
|
setSortMenuIndex(0);
|
|
1608
1964
|
return;
|
|
1609
1965
|
}
|
|
1610
1966
|
if (input === "v" || input === "V") {
|
|
1967
|
+
clearScreen();
|
|
1611
1968
|
setIsSortMenuOpen(false);
|
|
1612
1969
|
setIsVisibilityMenuOpen(true);
|
|
1613
1970
|
setSortMenuIndex(0);
|
|
@@ -1711,10 +2068,11 @@ var App = ({
|
|
|
1711
2068
|
// src/index.tsx
|
|
1712
2069
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
1713
2070
|
var bootstrap = async () => {
|
|
1714
|
-
const
|
|
2071
|
+
const isWindows = process2.platform === "win32";
|
|
2072
|
+
const unityHubReader = isWindows ? new WinUnityHubProjectsReader() : new MacUnityHubProjectsReader();
|
|
1715
2073
|
const gitRepositoryInfoReader = new GitRepositoryInfoReader();
|
|
1716
2074
|
const lockStatusReader = new UnityLockStatusReader();
|
|
1717
|
-
const unityProcessReader = new MacUnityProcessReader();
|
|
2075
|
+
const unityProcessReader = isWindows ? new WinUnityProcessReader() : new MacUnityProcessReader();
|
|
1718
2076
|
const unityTempDirectoryCleaner = new UnityTempDirectoryCleaner();
|
|
1719
2077
|
const listProjectsUseCase = new ListProjectsUseCase(
|
|
1720
2078
|
unityHubReader,
|
|
@@ -1723,10 +2081,10 @@ var bootstrap = async () => {
|
|
|
1723
2081
|
lockStatusReader,
|
|
1724
2082
|
unityProcessReader
|
|
1725
2083
|
);
|
|
1726
|
-
const editorPathResolver = new MacEditorPathResolver();
|
|
2084
|
+
const editorPathResolver = isWindows ? new WinEditorPathResolver() : new MacEditorPathResolver();
|
|
1727
2085
|
const processLauncher = new NodeProcessLauncher();
|
|
1728
2086
|
const lockChecker = new UnityLockChecker(unityProcessReader, unityTempDirectoryCleaner);
|
|
1729
|
-
const unityProcessTerminator = new MacUnityProcessTerminator();
|
|
2087
|
+
const unityProcessTerminator = isWindows ? new WinUnityProcessTerminator() : new MacUnityProcessTerminator();
|
|
1730
2088
|
const launchProjectUseCase = new LaunchProjectUseCase(
|
|
1731
2089
|
editorPathResolver,
|
|
1732
2090
|
processLauncher,
|
|
@@ -1741,6 +2099,23 @@ var bootstrap = async () => {
|
|
|
1741
2099
|
);
|
|
1742
2100
|
const useGitRootName = !process2.argv.includes("--no-git-root-name");
|
|
1743
2101
|
try {
|
|
2102
|
+
const rawModeSupported = Boolean(
|
|
2103
|
+
process2.stdin.isTTY && typeof process2.stdin.setRawMode === "function"
|
|
2104
|
+
);
|
|
2105
|
+
if (!rawModeSupported) {
|
|
2106
|
+
const message = [
|
|
2107
|
+
"\u3053\u306E\u7AEF\u672B\u3067\u306F\u5BFE\u8A71\u5165\u529B\uFF08Raw mode\uFF09\u304C\u4F7F\u3048\u307E\u305B\u3093\u3002",
|
|
2108
|
+
"PowerShell / cmd.exe \u3067\u5B9F\u884C\u3059\u308B\u304B\u3001ConPTY \u30D9\u30FC\u30B9\u306E\u30BF\u30FC\u30DF\u30CA\u30EB\uFF08Windows Terminal, VS Code/Cursor \u306E\u7D71\u5408\u30BF\u30FC\u30DF\u30CA\u30EB\uFF09\u3067 Git Bash \u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
2109
|
+
"MinTTY \u306E Git Bash \u3067\u306F\u6B21\u306E\u3044\u305A\u308C\u304B\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044:",
|
|
2110
|
+
" - winpty cmd.exe /c npx unity-hub-cli",
|
|
2111
|
+
" - winpty powershell.exe -NoProfile -Command npx unity-hub-cli",
|
|
2112
|
+
"\uFF08\u30D3\u30EB\u30C9\u6E08\u307F\u306E\u5834\u5408\uFF09npm run build && winpty node dist/index.js",
|
|
2113
|
+
"\u8A73\u3057\u304F: https://github.com/vadimdemedes/ink/#israwmodesupported"
|
|
2114
|
+
].join("\n");
|
|
2115
|
+
console.error(message);
|
|
2116
|
+
process2.exitCode = 1;
|
|
2117
|
+
return;
|
|
2118
|
+
}
|
|
1744
2119
|
const projects = await listProjectsUseCase.execute();
|
|
1745
2120
|
const { waitUntilExit } = render(
|
|
1746
2121
|
/* @__PURE__ */ jsx7(
|