uloop-cli 0.65.0 → 0.66.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/cli.bundle.cjs +103 -41
- package/dist/cli.bundle.cjs.map +3 -3
- package/package.json +8 -5
- package/src/__tests__/port-resolver.test.ts +31 -1
- package/src/cli.ts +35 -9
- package/src/commands/focus-window.ts +20 -2
- package/src/default-tools.json +1 -1
- package/src/execute-tool.ts +15 -10
- package/src/port-resolver.ts +42 -4
- package/src/project-root.ts +2 -2
- package/src/skills/skill-definitions/cli-only/uloop-focus-window/SKILL.md +7 -0
- package/src/version.ts +1 -1
package/dist/cli.bundle.cjs
CHANGED
|
@@ -5486,13 +5486,13 @@ var DirectUnityClient = class {
|
|
|
5486
5486
|
requestId = 0;
|
|
5487
5487
|
receiveBuffer = Buffer.alloc(0);
|
|
5488
5488
|
async connect() {
|
|
5489
|
-
return new Promise((
|
|
5489
|
+
return new Promise((resolve6, reject) => {
|
|
5490
5490
|
this.socket = new net.Socket();
|
|
5491
5491
|
this.socket.on("error", (error) => {
|
|
5492
5492
|
reject(new Error(`Connection error: ${error.message}`));
|
|
5493
5493
|
});
|
|
5494
5494
|
this.socket.connect(this.port, this.host, () => {
|
|
5495
|
-
|
|
5495
|
+
resolve6();
|
|
5496
5496
|
});
|
|
5497
5497
|
});
|
|
5498
5498
|
}
|
|
@@ -5508,7 +5508,7 @@ var DirectUnityClient = class {
|
|
|
5508
5508
|
};
|
|
5509
5509
|
const requestJson = JSON.stringify(request);
|
|
5510
5510
|
const framedMessage = createFrame(requestJson);
|
|
5511
|
-
return new Promise((
|
|
5511
|
+
return new Promise((resolve6, reject) => {
|
|
5512
5512
|
const socket = this.socket;
|
|
5513
5513
|
const cleanup = () => {
|
|
5514
5514
|
clearTimeout(timeoutId);
|
|
@@ -5545,7 +5545,7 @@ var DirectUnityClient = class {
|
|
|
5545
5545
|
reject(new Error(`Unity error: ${response.error.message}`));
|
|
5546
5546
|
return;
|
|
5547
5547
|
}
|
|
5548
|
-
|
|
5548
|
+
resolve6(response.result);
|
|
5549
5549
|
};
|
|
5550
5550
|
const onError = (error) => {
|
|
5551
5551
|
cleanup();
|
|
@@ -5759,13 +5759,41 @@ function resolvePortFromUnitySettings(settings) {
|
|
|
5759
5759
|
}
|
|
5760
5760
|
return null;
|
|
5761
5761
|
}
|
|
5762
|
-
|
|
5762
|
+
function validateProjectPath(projectPath) {
|
|
5763
|
+
const resolved = (0, import_path2.resolve)(projectPath);
|
|
5764
|
+
if (!(0, import_fs2.existsSync)(resolved)) {
|
|
5765
|
+
throw new Error(`Path does not exist: ${resolved}`);
|
|
5766
|
+
}
|
|
5767
|
+
if (!isUnityProject(resolved)) {
|
|
5768
|
+
throw new Error(`Not a Unity project (Assets/ or ProjectSettings/ not found): ${resolved}`);
|
|
5769
|
+
}
|
|
5770
|
+
if (!hasUloopInstalled(resolved)) {
|
|
5771
|
+
throw new Error(
|
|
5772
|
+
`uLoopMCP is not installed in this project (UserSettings/UnityMcpSettings.json not found): ${resolved}`
|
|
5773
|
+
);
|
|
5774
|
+
}
|
|
5775
|
+
return resolved;
|
|
5776
|
+
}
|
|
5777
|
+
async function resolveUnityPort(explicitPort, projectPath) {
|
|
5778
|
+
if (explicitPort !== void 0 && projectPath !== void 0) {
|
|
5779
|
+
throw new Error("Cannot specify both --port and --project-path. Use one or the other.");
|
|
5780
|
+
}
|
|
5763
5781
|
if (explicitPort !== void 0) {
|
|
5764
5782
|
return explicitPort;
|
|
5765
5783
|
}
|
|
5784
|
+
if (projectPath !== void 0) {
|
|
5785
|
+
const resolved = validateProjectPath(projectPath);
|
|
5786
|
+
const settingsPort2 = await readPortFromSettings(resolved);
|
|
5787
|
+
if (settingsPort2 !== null) {
|
|
5788
|
+
return settingsPort2;
|
|
5789
|
+
}
|
|
5790
|
+
return DEFAULT_PORT;
|
|
5791
|
+
}
|
|
5766
5792
|
const projectRoot = findUnityProjectRoot();
|
|
5767
5793
|
if (projectRoot === null) {
|
|
5768
|
-
throw new Error(
|
|
5794
|
+
throw new Error(
|
|
5795
|
+
"Unity project not found. Use --port or --project-path option to specify the target."
|
|
5796
|
+
);
|
|
5769
5797
|
}
|
|
5770
5798
|
const settingsPort = await readPortFromSettings(projectRoot);
|
|
5771
5799
|
if (settingsPort !== null) {
|
|
@@ -5799,7 +5827,7 @@ var import_path3 = require("path");
|
|
|
5799
5827
|
|
|
5800
5828
|
// src/default-tools.json
|
|
5801
5829
|
var default_tools_default = {
|
|
5802
|
-
version: "0.
|
|
5830
|
+
version: "0.66.1",
|
|
5803
5831
|
tools: [
|
|
5804
5832
|
{
|
|
5805
5833
|
name: "compile",
|
|
@@ -6247,7 +6275,7 @@ function getCachedServerVersion() {
|
|
|
6247
6275
|
}
|
|
6248
6276
|
|
|
6249
6277
|
// src/version.ts
|
|
6250
|
-
var VERSION = "0.
|
|
6278
|
+
var VERSION = "0.66.1";
|
|
6251
6279
|
|
|
6252
6280
|
// src/spinner.ts
|
|
6253
6281
|
var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
@@ -6383,11 +6411,11 @@ function tryReadCompileResult(projectRoot, requestId) {
|
|
|
6383
6411
|
}
|
|
6384
6412
|
}
|
|
6385
6413
|
function canSendRequestToUnity(port) {
|
|
6386
|
-
return new Promise((
|
|
6414
|
+
return new Promise((resolve6) => {
|
|
6387
6415
|
const socket = new net2.Socket();
|
|
6388
6416
|
const timer = setTimeout(() => {
|
|
6389
6417
|
socket.destroy();
|
|
6390
|
-
|
|
6418
|
+
resolve6(false);
|
|
6391
6419
|
}, READINESS_CHECK_TIMEOUT_MS);
|
|
6392
6420
|
const cleanup = () => {
|
|
6393
6421
|
clearTimeout(timer);
|
|
@@ -6410,21 +6438,21 @@ function canSendRequestToUnity(port) {
|
|
|
6410
6438
|
const sepIndex = buffer.indexOf(HEADER_SEPARATOR2);
|
|
6411
6439
|
if (sepIndex !== -1) {
|
|
6412
6440
|
cleanup();
|
|
6413
|
-
|
|
6441
|
+
resolve6(true);
|
|
6414
6442
|
}
|
|
6415
6443
|
});
|
|
6416
6444
|
socket.on("error", () => {
|
|
6417
6445
|
cleanup();
|
|
6418
|
-
|
|
6446
|
+
resolve6(false);
|
|
6419
6447
|
});
|
|
6420
6448
|
socket.on("close", () => {
|
|
6421
6449
|
clearTimeout(timer);
|
|
6422
|
-
|
|
6450
|
+
resolve6(false);
|
|
6423
6451
|
});
|
|
6424
6452
|
});
|
|
6425
6453
|
}
|
|
6426
6454
|
function sleep(ms) {
|
|
6427
|
-
return new Promise((
|
|
6455
|
+
return new Promise((resolve6) => setTimeout(resolve6, ms));
|
|
6428
6456
|
}
|
|
6429
6457
|
async function waitForCompileCompletion(options) {
|
|
6430
6458
|
const startTime = Date.now();
|
|
@@ -6565,8 +6593,8 @@ function checkServerVersion(result) {
|
|
|
6565
6593
|
printVersionWarning(VERSION, serverVersion);
|
|
6566
6594
|
}
|
|
6567
6595
|
}
|
|
6568
|
-
function checkUnityBusyState() {
|
|
6569
|
-
const projectRoot = findUnityProjectRoot();
|
|
6596
|
+
function checkUnityBusyState(projectPath) {
|
|
6597
|
+
const projectRoot = projectPath !== void 0 ? validateProjectPath(projectPath) : findUnityProjectRoot();
|
|
6570
6598
|
if (projectRoot === null) {
|
|
6571
6599
|
return;
|
|
6572
6600
|
}
|
|
@@ -6592,7 +6620,7 @@ async function executeToolCommand(toolName, params, globalOptions) {
|
|
|
6592
6620
|
}
|
|
6593
6621
|
portNumber = parsed;
|
|
6594
6622
|
}
|
|
6595
|
-
const port = await resolveUnityPort(portNumber);
|
|
6623
|
+
const port = await resolveUnityPort(portNumber, globalOptions.projectPath);
|
|
6596
6624
|
const compileOptions = getCompileExecutionOptions(toolName, params);
|
|
6597
6625
|
const shouldWaitForDomainReload = compileOptions.waitForDomainReload;
|
|
6598
6626
|
const compileRequestId = shouldWaitForDomainReload ? ensureCompileRequestId(params) : void 0;
|
|
@@ -6600,10 +6628,10 @@ async function executeToolCommand(toolName, params, globalOptions) {
|
|
|
6600
6628
|
const spinner = createSpinner("Connecting to Unity...");
|
|
6601
6629
|
let lastError;
|
|
6602
6630
|
let immediateResult;
|
|
6603
|
-
const projectRoot = findUnityProjectRoot();
|
|
6631
|
+
const projectRoot = globalOptions.projectPath !== void 0 ? validateProjectPath(globalOptions.projectPath) : findUnityProjectRoot();
|
|
6604
6632
|
let requestDispatched = false;
|
|
6605
6633
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
6606
|
-
checkUnityBusyState();
|
|
6634
|
+
checkUnityBusyState(globalOptions.projectPath);
|
|
6607
6635
|
const client = new DirectUnityClient(port);
|
|
6608
6636
|
try {
|
|
6609
6637
|
await client.connect();
|
|
@@ -6717,12 +6745,12 @@ async function listAvailableTools(globalOptions) {
|
|
|
6717
6745
|
}
|
|
6718
6746
|
portNumber = parsed;
|
|
6719
6747
|
}
|
|
6720
|
-
const port = await resolveUnityPort(portNumber);
|
|
6748
|
+
const port = await resolveUnityPort(portNumber, globalOptions.projectPath);
|
|
6721
6749
|
const restoreStdin = suppressStdinEcho();
|
|
6722
6750
|
const spinner = createSpinner("Connecting to Unity...");
|
|
6723
6751
|
let lastError;
|
|
6724
6752
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
6725
|
-
checkUnityBusyState();
|
|
6753
|
+
checkUnityBusyState(globalOptions.projectPath);
|
|
6726
6754
|
const client = new DirectUnityClient(port);
|
|
6727
6755
|
try {
|
|
6728
6756
|
await client.connect();
|
|
@@ -6774,12 +6802,12 @@ async function syncTools(globalOptions) {
|
|
|
6774
6802
|
}
|
|
6775
6803
|
portNumber = parsed;
|
|
6776
6804
|
}
|
|
6777
|
-
const port = await resolveUnityPort(portNumber);
|
|
6805
|
+
const port = await resolveUnityPort(portNumber, globalOptions.projectPath);
|
|
6778
6806
|
const restoreStdin = suppressStdinEcho();
|
|
6779
6807
|
const spinner = createSpinner("Connecting to Unity...");
|
|
6780
6808
|
let lastError;
|
|
6781
6809
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
6782
|
-
checkUnityBusyState();
|
|
6810
|
+
checkUnityBusyState(globalOptions.projectPath);
|
|
6783
6811
|
const client = new DirectUnityClient(port);
|
|
6784
6812
|
try {
|
|
6785
6813
|
await client.connect();
|
|
@@ -8212,7 +8240,7 @@ async function waitForProcessExit(pid, timeoutMs) {
|
|
|
8212
8240
|
if (!isProcessAlive(pid)) {
|
|
8213
8241
|
return true;
|
|
8214
8242
|
}
|
|
8215
|
-
await new Promise((
|
|
8243
|
+
await new Promise((resolve6) => setTimeout(resolve6, KILL_POLL_INTERVAL_MS));
|
|
8216
8244
|
}
|
|
8217
8245
|
return false;
|
|
8218
8246
|
}
|
|
@@ -8381,7 +8409,7 @@ async function waitForLockfile(projectPath) {
|
|
|
8381
8409
|
if ((0, import_node_fs2.existsSync)(lockfilePath)) {
|
|
8382
8410
|
return;
|
|
8383
8411
|
}
|
|
8384
|
-
await new Promise((
|
|
8412
|
+
await new Promise((resolve6) => setTimeout(resolve6, LOCKFILE_POLL_INTERVAL_MS));
|
|
8385
8413
|
}
|
|
8386
8414
|
console.warn("Unity launched, but UnityLockfile was not detected within 5s.");
|
|
8387
8415
|
}
|
|
@@ -8412,7 +8440,7 @@ async function launch(opts) {
|
|
|
8412
8440
|
if (unityArgs.length > 0) {
|
|
8413
8441
|
args.push(...unityArgs);
|
|
8414
8442
|
}
|
|
8415
|
-
return new Promise((
|
|
8443
|
+
return new Promise((resolve6, reject) => {
|
|
8416
8444
|
const child = (0, import_node_child_process.spawn)(unityPath, args, {
|
|
8417
8445
|
stdio: "ignore",
|
|
8418
8446
|
detached: true,
|
|
@@ -8429,7 +8457,7 @@ async function launch(opts) {
|
|
|
8429
8457
|
const handleSpawn = () => {
|
|
8430
8458
|
child.removeListener("error", handleError);
|
|
8431
8459
|
child.unref();
|
|
8432
|
-
|
|
8460
|
+
resolve6();
|
|
8433
8461
|
};
|
|
8434
8462
|
child.once("error", handleError);
|
|
8435
8463
|
child.once("spawn", handleSpawn);
|
|
@@ -8537,8 +8565,24 @@ async function runLaunchCommand(projectPath, options) {
|
|
|
8537
8565
|
|
|
8538
8566
|
// src/commands/focus-window.ts
|
|
8539
8567
|
function registerFocusWindowCommand(program3) {
|
|
8540
|
-
program3.command("focus-window").description("Bring Unity Editor window to front using OS-level commands").action(async () => {
|
|
8541
|
-
|
|
8568
|
+
program3.command("focus-window").description("Bring Unity Editor window to front using OS-level commands").option("--project-path <path>", "Unity project path").action(async (options) => {
|
|
8569
|
+
let projectRoot;
|
|
8570
|
+
if (options.projectPath !== void 0) {
|
|
8571
|
+
try {
|
|
8572
|
+
projectRoot = validateProjectPath(options.projectPath);
|
|
8573
|
+
} catch (error) {
|
|
8574
|
+
console.error(
|
|
8575
|
+
JSON.stringify({
|
|
8576
|
+
Success: false,
|
|
8577
|
+
Message: error instanceof Error ? error.message : String(error)
|
|
8578
|
+
})
|
|
8579
|
+
);
|
|
8580
|
+
process.exit(1);
|
|
8581
|
+
return;
|
|
8582
|
+
}
|
|
8583
|
+
} else {
|
|
8584
|
+
projectRoot = findUnityProjectRoot();
|
|
8585
|
+
}
|
|
8542
8586
|
if (projectRoot === null) {
|
|
8543
8587
|
console.error(
|
|
8544
8588
|
JSON.stringify({
|
|
@@ -8596,11 +8640,11 @@ var program2 = new Command();
|
|
|
8596
8640
|
program2.name("uloop").description("Unity MCP CLI - Direct communication with Unity Editor").version(VERSION, "-v, --version", "Output the version number");
|
|
8597
8641
|
program2.option("--list-commands", "List all command names (for shell completion)");
|
|
8598
8642
|
program2.option("--list-options <cmd>", "List options for a command (for shell completion)");
|
|
8599
|
-
program2.command("list").description("List all available tools from Unity").option("-p, --port <port>", "Unity TCP port").action(async (options) => {
|
|
8600
|
-
await runWithErrorHandling(() => listAvailableTools(options));
|
|
8643
|
+
program2.command("list").description("List all available tools from Unity").option("-p, --port <port>", "Unity TCP port").option("--project-path <path>", "Unity project path").action(async (options) => {
|
|
8644
|
+
await runWithErrorHandling(() => listAvailableTools(extractGlobalOptions(options)));
|
|
8601
8645
|
});
|
|
8602
|
-
program2.command("sync").description("Sync tool definitions from Unity to local cache").option("-p, --port <port>", "Unity TCP port").action(async (options) => {
|
|
8603
|
-
await runWithErrorHandling(() => syncTools(options));
|
|
8646
|
+
program2.command("sync").description("Sync tool definitions from Unity to local cache").option("-p, --port <port>", "Unity TCP port").option("--project-path <path>", "Unity project path").action(async (options) => {
|
|
8647
|
+
await runWithErrorHandling(() => syncTools(extractGlobalOptions(options)));
|
|
8604
8648
|
});
|
|
8605
8649
|
program2.command("completion").description("Setup shell completion").option("--install", "Install completion to shell config file").option("--shell <type>", "Shell type: bash, zsh, or powershell").action((options) => {
|
|
8606
8650
|
handleCompletion(options.install ?? false, options.shell);
|
|
@@ -8608,8 +8652,11 @@ program2.command("completion").description("Setup shell completion").option("--i
|
|
|
8608
8652
|
program2.command("update").description("Update uloop CLI to the latest version").action(() => {
|
|
8609
8653
|
updateCli();
|
|
8610
8654
|
});
|
|
8611
|
-
program2.command("fix").description("Clean up stale lock files that may prevent CLI from connecting").action(() => {
|
|
8612
|
-
|
|
8655
|
+
program2.command("fix").description("Clean up stale lock files that may prevent CLI from connecting").option("--project-path <path>", "Unity project path").action(async (options) => {
|
|
8656
|
+
await runWithErrorHandling(() => {
|
|
8657
|
+
cleanupLockFiles(options.projectPath);
|
|
8658
|
+
return Promise.resolve();
|
|
8659
|
+
});
|
|
8613
8660
|
});
|
|
8614
8661
|
registerSkillsCommand(program2);
|
|
8615
8662
|
registerLaunchCommand(program2);
|
|
@@ -8632,6 +8679,7 @@ function registerToolCommand(tool) {
|
|
|
8632
8679
|
}
|
|
8633
8680
|
}
|
|
8634
8681
|
cmd.option("-p, --port <port>", "Unity TCP port");
|
|
8682
|
+
cmd.option("--project-path <path>", "Unity project path");
|
|
8635
8683
|
cmd.action(async (options) => {
|
|
8636
8684
|
const params = buildParams(options, properties);
|
|
8637
8685
|
if (tool.name === "execute-dynamic-code" && params["Code"]) {
|
|
@@ -8738,7 +8786,8 @@ function convertValue(value, propInfo) {
|
|
|
8738
8786
|
}
|
|
8739
8787
|
function extractGlobalOptions(options) {
|
|
8740
8788
|
return {
|
|
8741
|
-
port: options["port"]
|
|
8789
|
+
port: options["port"],
|
|
8790
|
+
projectPath: options["projectPath"]
|
|
8742
8791
|
};
|
|
8743
8792
|
}
|
|
8744
8793
|
function isConnectionError(message) {
|
|
@@ -8973,8 +9022,8 @@ function updateCli() {
|
|
|
8973
9022
|
});
|
|
8974
9023
|
}
|
|
8975
9024
|
var LOCK_FILES = ["compiling.lock", "domainreload.lock", "serverstarting.lock"];
|
|
8976
|
-
function cleanupLockFiles() {
|
|
8977
|
-
const projectRoot = findUnityProjectRoot();
|
|
9025
|
+
function cleanupLockFiles(projectPath) {
|
|
9026
|
+
const projectRoot = projectPath !== void 0 ? validateProjectPath(projectPath) : findUnityProjectRoot();
|
|
8978
9027
|
if (projectRoot === null) {
|
|
8979
9028
|
console.error("Could not find Unity project root.");
|
|
8980
9029
|
process.exit(1);
|
|
@@ -9102,20 +9151,33 @@ function shouldSkipAutoSync(cmdName, args) {
|
|
|
9102
9151
|
return args.some((arg) => NO_SYNC_FLAGS.includes(arg));
|
|
9103
9152
|
}
|
|
9104
9153
|
function extractSyncGlobalOptions(args) {
|
|
9154
|
+
const options = {};
|
|
9105
9155
|
for (let i = 0; i < args.length; i++) {
|
|
9106
9156
|
const arg = args[i];
|
|
9107
9157
|
if (arg === "--port" || arg === "-p") {
|
|
9108
9158
|
const nextArg = args[i + 1];
|
|
9109
9159
|
if (nextArg !== void 0 && !nextArg.startsWith("-")) {
|
|
9110
|
-
|
|
9160
|
+
options.port = nextArg;
|
|
9111
9161
|
}
|
|
9112
9162
|
continue;
|
|
9113
9163
|
}
|
|
9114
9164
|
if (arg.startsWith("--port=")) {
|
|
9115
|
-
|
|
9165
|
+
options.port = arg.slice("--port=".length);
|
|
9166
|
+
continue;
|
|
9167
|
+
}
|
|
9168
|
+
if (arg === "--project-path") {
|
|
9169
|
+
const nextArg = args[i + 1];
|
|
9170
|
+
if (nextArg !== void 0 && !nextArg.startsWith("-")) {
|
|
9171
|
+
options.projectPath = nextArg;
|
|
9172
|
+
}
|
|
9173
|
+
continue;
|
|
9174
|
+
}
|
|
9175
|
+
if (arg.startsWith("--project-path=")) {
|
|
9176
|
+
options.projectPath = arg.slice("--project-path=".length);
|
|
9177
|
+
continue;
|
|
9116
9178
|
}
|
|
9117
9179
|
}
|
|
9118
|
-
return
|
|
9180
|
+
return options;
|
|
9119
9181
|
}
|
|
9120
9182
|
async function main() {
|
|
9121
9183
|
if (handleCompletionOptions()) {
|