A TypeScript library for programmatically interacting with Unity Hub and Unity Editor command line interfaces.
npm install @notask/unity-cli-toolsThis package publishes separate builds for both ES Modules (ESM) and CommonJS (CJS). You can consume it either way:
ESM (Node and bundlers):
import { UnityHub } from "@notask/unity-cli-tools";CJS:
const { UnityHub } = require("@notask/unity-cli-tools");- Node.js 20+
- Unity Hub installed
- Unity Editor (for editor operations)
Unity CLI Tools provides two main modules:
UnityHub- For interacting with Unity HubUnityEditor- For interacting with Unity Editor
All operations return a Result<T, E> type instead of throwing exceptions. This provides:
- Type-safe error handling - Errors are explicit in the return type
- Rich error context - Each error includes error codes, messages, and contextual data
- Composable operations - Chain operations with
map,andThen, etc. - No unexpected exceptions - All errors are handled explicitly
import { UnityHub, isOk, isErr } from "@notask/unity-cli-tools";
const result = await UnityHub.getUnityInstallations();
if (isOk(result)) {
// Type-safe access to the value
const installations = result.value;
console.log("Installed versions:", Object.keys(installations));
} else {
// Type-safe access to the error
console.error(`Error [${result.error.code}]:`, result.error.message);
console.error("Context:", result.error.context);
}import { UnityHub } from "@notask/unity-cli-tools";
// Check if Unity Hub is available
const isAvailable = await UnityHub.isUnityHubAvailable();import { UnityHub, isOk } from "@notask/unity-cli-tools";
// Get all installed Unity versions
const result = await UnityHub.getUnityInstallations();
if (isOk(result)) {
const installations = result.value;
// Returns: { '2022.3.60f1': 'C:/Program Files/Unity/Hub/Editor/2022.3.60f1', ... }
console.log("Installed versions:", Object.keys(installations));
} else {
console.error("Failed to get installations:", result.error.message);
}import { UnityHub, UnityModules, isOk } from "@notask/unity-cli-tools";
// Install a new Unity version
const result = await UnityHub.addEditor("2022.3.60f1");
if (isOk(result)) {
const installer = result.value;
// Wait for installation to complete
await installer.completed;
console.log("Installation completed!");
} else {
console.error("Failed to start installation:", result.error.message);
}
// Install with specific modules
const resultWithModules = await UnityHub.addEditor(
"2022.3.60f1",
[UnityModules.AndroidBuildSupport, UnityModules.WebGLBuildSupport]
);
// Add modules to existing installation
const moduleResult = await UnityHub.addModule(
"2022.3.60f1",
[UnityModules.IOSBuildSupport]
);
if (isOk(moduleResult)) {
console.log("Module installation started");
}import { UnityHub, UnityModules, InstallerEventType, isOk } from "@notask/unity-cli-tools";
// Install with event tracking
const result = await UnityHub.addEditor("2022.3.60f1");
if (!isOk(result)) {
console.error("Failed to start installation:", result.error);
return;
}
const installer = result.value;
// Listen to progress events
installer.on(InstallerEventType.Progress, (events) => {
console.log("Progress:", events.map(e => `${e.module}: ${e.status} ${e.progress || 0}%`));
});
installer.on(InstallerEventType.Error, (errorEvents) => {
console.error("Installation error:", errorEvents);
});
installer.on(InstallerEventType.Completed, (events) => {
console.log("Installation completed!");
});
// Or use the promise
try {
const completedEvents = await installer.completed;
console.log("Installation finished:", completedEvents);
} catch (error) {
console.error("Installation failed:", error);
}
// Cancel installation if needed
installer.Cancel();import { UnityHub, isOk } from "@notask/unity-cli-tools";
// Get projects from Unity Hub
const result = await UnityHub.getProjects();
if (isOk(result)) {
const projects = result.value;
// Returns: [{ name: 'ProjectName', path: '/path/to/project', version: '2022.3.60f1' }, ...]
console.log("Projects:", projects);
}
// Get default projects directory
const dirResult = await UnityHub.getDefaultProjectsDirectory();
if (isOk(dirResult)) {
const defaultDir = dirResult.value; // string | null
console.log("Default directory:", defaultDir);
}import { UnityHub, isOk } from "@notask/unity-cli-tools";
// Execute any Hub command directly
const result = await UnityHub.execUnityHubCommand(["editors", "-r"]);
if (isOk(result)) {
console.log(result.value.stdout);
console.log("Exit code:", result.value.exitCode);
} else {
console.error("Command failed:", result.error.stderr);
}UnityEditor provides an interface for automating tasks directly in the Unity Editor:
import { UnityEditor } from "@notask/unity-cli-tools";
// Get the executable path and verify installation
const unityPath = UnityEditor.getUnityExecutablePath("2022.3.15f1");
const isInstalled = await UnityEditor.isUnityVersionInstalled("2022.3.15f1");
console.log(`Installed: ${isInstalled}`, unityPath);import { UnityEditor, isOk } from "@notask/unity-cli-tools";
const editorInfo = { version: "2022.3.15f1", path: unityPath };
const result = await UnityEditor.execUnityEditorCommand(
editorInfo,
["-batchmode", "-quit", "-projectPath", "/path/to/project"]
);
if (isOk(result)) {
console.log("Command output:", result.value.stdout);
console.log("Exit code:", result.value.exitCode);
} else {
console.error("Command failed:", result.error.message);
console.error("Error output:", result.error.stderr);
}import { UnityEditor, ProjectInfo, isOk } from "@notask/unity-cli-tools";
const projectInfo: ProjectInfo = {
projectName: "MyAwesomeGame",
projectPath: "/path/to/new/project",
editorVersion: "2022.3.15f1"
};
// Create project
const createResult = await UnityEditor.createProject(projectInfo);
if (isOk(createResult)) {
console.log("Project created successfully!");
// Open project
const openResult = await UnityEditor.openProject(projectInfo, true, true);
if (isOk(openResult)) {
console.log("Project opened successfully!");
}
} else {
console.error("Failed to create project:", createResult.error.message);
}import { UnityEditor, ProjectInfo, TestMode, isOk } from "@notask/unity-cli-tools";
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: "/path/to/project",
editorVersion: "2022.3.15f1"
};
const result = await UnityEditor.runTests(projectInfo, TestMode.EditMode);
if (isOk(result)) {
console.log("All tests passed!");
console.log("Test output:", result.value);
} else {
console.error("Tests failed:", result.error.message);
// UnityTestError includes the test output
if (result.error.code === "UNITY_TEST_ERROR") {
console.error("Test output:", result.error.testOutput);
}
}import { UnityEditor, ProjectInfo, isOk } from "@notask/unity-cli-tools";
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: "/path/to/project",
editorVersion: "2022.3.15f1"
};
// Activate license
const activateResult = await UnityEditor.activateLicense(
projectInfo,
"XXXX-XXXX-XXXX-XXXX-XXXX",
"user@example.com",
"password"
);
if (isOk(activateResult)) {
console.log("License activated successfully!");
// Do work...
// Return license when done
const returnResult = await UnityEditor.returnLicense(projectInfo);
if (isOk(returnResult)) {
console.log("License returned successfully!");
}
} else {
console.error("License activation failed:", activateResult.error.message);
}import { UnityEditor, ProjectInfo, isOk } from "@notask/unity-cli-tools";
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: "/path/to/project",
editorVersion: "2022.3.15f1"
};
// Export package
const exportResult = await UnityEditor.exportPackage(
projectInfo,
["Assets/UI", "Assets/Scripts"],
"/path/to/output/MyPackage.unitypackage"
);
if (isOk(exportResult)) {
console.log("Package exported successfully!");
}
// Import package
const importResult = await UnityEditor.importPackage(
projectInfo,
"/path/to/downloads/OtherPackage.unitypackage"
);
if (isOk(importResult)) {
console.log("Package imported successfully!");
}import { UnityEditor, ProjectInfo, isOk } from "@notask/unity-cli-tools";
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: "/path/to/project",
editorVersion: "2022.3.15f1"
};
const result = await UnityEditor.executeMethod(
projectInfo,
"MyCompany.BuildTools.PerformBuild"
);
if (isOk(result)) {
console.log("Method executed successfully!");
console.log("Output:", result.value.stdout);
} else {
console.error("Method execution failed:", result.error.message);
}The library provides specific error types for different failure scenarios:
import {
UnityHubNotFoundError,
UnityEditorNotFoundError,
UnityCommandError,
UnityInstallationError,
UnityProjectError,
UnityLicenseError,
UnityPackageError,
UnityTestError,
InvalidArgumentError,
} from "@notask/unity-cli-tools";Each error includes:
code- Error code for programmatic handlingmessage- Human-readable error messagecontext- Additional contextual informationstack- Stack trace
Basic Pattern Matching:
const result = await UnityHub.getUnityInstallations();
if (result.success) {
// Handle success
const installations = result.value;
} else {
// Handle error
const error = result.error;
console.error(`[${error.code}] ${error.message}`);
}Type Guards:
import { isOk, isErr } from "@notask/unity-cli-tools";
const result = await UnityHub.getUnityInstallations();
if (isOk(result)) {
// TypeScript knows result.value exists
const installations = result.value;
}
if (isErr(result)) {
// TypeScript knows result.error exists
const error = result.error;
}Specific Error Handling:
const result = await UnityHub.getUnityInstallations();
if (!result.success) {
switch (result.error.code) {
case "UNITY_HUB_NOT_FOUND":
console.error("Please install Unity Hub");
break;
case "UNITY_INSTALLATION_ERROR":
console.error("No Unity versions found");
break;
default:
console.error("Unexpected error:", result.error.message);
}
}Chaining Operations:
import { map, andThen } from "@notask/unity-cli-tools";
const result = await UnityHub.getUnityInstallations();
// Transform the result
const versions = map(result, (installations) => Object.keys(installations));
// Chain operations
const firstVersion = andThen(result, async (installations) => {
const versions = Object.keys(installations);
if (versions.length === 0) {
return err(new UnityInstallationError("No installations found"));
}
return ok(versions[0]);
});Unwrapping (Use with Caution):
import { unwrap, unwrapOr } from "@notask/unity-cli-tools";
// Throws if result is an error
const installations = unwrap(await UnityHub.getUnityInstallations());
// Returns default value if result is an error
const installations = unwrapOr(await UnityHub.getUnityInstallations(), {});| Constant | Description |
|---|---|
Documentation |
Unity documentation |
AndroidBuildSupport |
Android platform support |
AndroidSDKNDKTools |
Android SDK/NDK tools |
OpenJDK |
Java Development Kit |
IOSBuildSupport |
iOS platform support |
TvOSBuildSupport |
tvOS platform support |
LinuxBuildSupportMono |
Linux Mono support |
LinuxBuildSupportIL2CPP |
Linux IL2CPP support |
WebGLBuildSupport |
WebGL platform support |
WindowsBuildSupport |
Windows platform support |
VuforiaAR |
Vuforia AR support |
WindowsBuildSupportMono |
Windows Mono support |
LuminBuildSupport |
Magic Leap support |
VisualStudioCommunity |
Visual Studio integration |
MacBuildSupportMono |
macOS Mono support |
MacBuildSupportIL2CPP |
macOS IL2CPP support |
UniversalWindowsPlatform |
UWP support |
UWPBuildSupportIL2CPP |
UWP IL2CPP support |
UWPBuildSupportDotNet |
UWP .NET support |
| Constant | Description |
|---|---|
Japanese |
Japanese language pack |
Korean |
Korean language pack |
ChineseSimplified |
Simplified Chinese language pack |
ChineseTraditional |
Traditional Chinese language pack |
Chinese |
Chinese language pack (legacy) |
| Constant | Description |
|---|---|
Queued |
Queued for download |
Validating |
Validating download |
InProgress |
Installation in progress |
Downloading |
Downloading installation files |
QueuedInstall |
Queued for install |
ValidatingInstall |
Validating installation |
Installing |
Installing |
Verifying |
Verifying installation |
Installed |
Installed successfully |
Error |
Installation error |
| Constant | Description |
|---|---|
Progress |
Installation progress update event |
Error |
Installation error event |
Completed |
Installation completed successfully event |
Cancelled |
Installation cancelled by user event |
| Code | Description |
|---|---|
UNITY_HUB_NOT_FOUND |
Unity Hub executable not found |
UNITY_EDITOR_NOT_FOUND |
Unity Editor version not found |
UNITY_COMMAND_ERROR |
Command execution failed |
UNITY_INSTALLATION_ERROR |
Installation operation failed |
UNITY_PROJECT_ERROR |
Project operation failed |
UNITY_LICENSE_ERROR |
License operation failed |
UNITY_PACKAGE_ERROR |
Package operation failed |
UNITY_TEST_ERROR |
Tests failed |
INVALID_ARGUMENT |
Invalid argument provided |
UNITY_HUB_PATH- Custom path to Unity Hub executable
The library automatically detects and uses the correct paths for:
- Windows
- macOS
- Linux
import { UnityHub, UnityModules, isOk } from "@notask/unity-cli-tools";
async function manageUnityInstallation() {
// Check if hub is available
const isAvailable = await UnityHub.isUnityHubAvailable();
if (!isAvailable) {
console.error("Unity Hub not found");
return;
}
// List installations
const installsResult = await UnityHub.getUnityInstallations();
if (!isOk(installsResult)) {
console.error("Failed to get installations:", installsResult.error.message);
return;
}
const installations = installsResult.value;
console.log("Installed versions:", Object.keys(installations));
// Install WebGL support for specific version
if (installations["2022.3.60f1"]) {
const moduleResult = await UnityHub.addModule(
"2022.3.60f1",
[UnityModules.WebGLBuildSupport]
);
if (isOk(moduleResult)) {
const installer = moduleResult.value;
await installer.completed;
console.log("WebGL support added");
}
}
// Get recent projects
const projectsResult = await UnityHub.getProjects();
if (isOk(projectsResult)) {
const projects = projectsResult.value;
console.log("Recent projects:", projects.map((p) => p.name));
}
}
manageUnityInstallation().catch(console.error);import { UnityEditor, ProjectInfo, TestMode, isOk } from "@notask/unity-cli-tools";
async function ciPipeline() {
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: process.cwd(),
editorVersion: "2022.3.15f1"
};
// Activate license
const licenseResult = await UnityEditor.activateLicense(
projectInfo,
process.env.UNITY_SERIAL!,
process.env.UNITY_EMAIL!,
process.env.UNITY_PASSWORD!
);
if (!isOk(licenseResult)) {
console.error("License activation failed:", licenseResult.error.message);
process.exit(1);
}
try {
// Run tests
const testResult = await UnityEditor.runTests(projectInfo, TestMode.EditMode);
if (!isOk(testResult)) {
console.error("Tests failed:", testResult.error.message);
if (testResult.error.code === "UNITY_TEST_ERROR") {
console.error("Test output:", testResult.error.testOutput);
}
process.exit(1);
}
console.log("All tests passed!");
// Build project
const buildResult = await UnityEditor.executeMethod(
projectInfo,
"BuildScript.PerformBuild"
);
if (!isOk(buildResult)) {
console.error("Build failed:", buildResult.error.message);
process.exit(1);
}
console.log("Build completed successfully!");
} finally {
// Always return license
await UnityEditor.returnLicense(projectInfo);
}
}
ciPipeline().catch(console.error);import { UnityHub, isOk } from "@notask/unity-cli-tools";
// List all editors with detailed output
const result = await UnityHub.execUnityHubCommand(["editors", "-a"], {
timeout: 30000, // 30 second timeout
env: { UNITY_HUB_VERBOSE: "1" }, // Custom environment variables
});
if (isOk(result)) {
// Process output
const editorList = result.value.stdout
.split("\n")
.filter((line) => line.includes("Unity Version"));
console.log("Editors:", editorList);
} else {
console.error("Command failed:", result.error.message);
}import { UnityHub, isOk } from "@notask/unity-cli-tools";
const result = await UnityHub.execUnityHubCommand(["editors", "-i"], {
onStdout: (line) => {
console.log("STDOUT:", line);
},
onStderr: (line) => {
console.error("STDERR:", line);
}
});
if (isOk(result)) {
console.log("Command completed");
}Version 2.x introduces breaking changes with the Result pattern:
Before (v1.x):
try {
const installations = await UnityHub.getUnityInstallations();
console.log(installations);
} catch (error) {
console.error(error);
}After (v2.x):
import { isOk } from "@notask/unity-cli-tools";
const result = await UnityHub.getUnityInstallations();
if (isOk(result)) {
console.log(result.value);
} else {
console.error(result.error.message);
}Method Return Type Changes:
| Method | Old Return Type | New Return Type |
|---|---|---|
getUnityInstallations() |
Promise<UnityInstallations> (throws) |
Promise<Result<UnityInstallations, Error>> |
runTests() |
Promise<{success: boolean, output: string}> |
Promise<Result<string, UnityTestError>> |
activateLicense() |
Promise<boolean> |
Promise<Result<void, UnityLicenseError>> |
createProject() |
Promise<boolean> |
Promise<Result<void, UnityProjectError>> |
This library is written in TypeScript and provides full type definitions:
import {
UnityHub,
UnityEditor,
ProjectInfo,
UnityModules,
TestMode,
Result,
UnityError,
isOk,
isErr,
} from "@notask/unity-cli-tools";
// All types are exported and fully typed
const projectInfo: ProjectInfo = {
projectName: "MyGame",
projectPath: "/path/to/project",
editorVersion: "2022.3.15f1"
};Contributions are welcome! Please feel free to submit a Pull Request.
MIT License - see LICENSE file for details