unity-mcp-cli 0.69.1 → 0.71.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.
@@ -15,6 +15,40 @@ export type ProgressEvent = {
15
15
  phase: 'dependencies-resolved';
16
16
  message: string;
17
17
  version: string;
18
+ } | {
19
+ phase: 'detecting-editor-version';
20
+ message: string;
21
+ } | {
22
+ phase: 'editors-located';
23
+ message: string;
24
+ found: boolean;
25
+ } | {
26
+ phase: 'editor-resolved';
27
+ message: string;
28
+ editorPath: string;
29
+ version?: string;
30
+ } | {
31
+ phase: 'connection-details';
32
+ message: string;
33
+ projectPath: string;
34
+ editorPath: string;
35
+ envVars: Record<string, string>;
36
+ } | {
37
+ phase: 'launching-editor';
38
+ message: string;
39
+ editorPath: string;
40
+ projectPath: string;
41
+ } | {
42
+ phase: 'editor-launched';
43
+ message: string;
44
+ pid?: number;
45
+ } | {
46
+ phase: 'launch-errors-dismissed';
47
+ message: string;
48
+ /** Button label that was clicked (e.g. `Ignore`). */
49
+ button: string;
50
+ /** Platform on which the dismiss was performed (`win32` | `darwin` | `linux`). */
51
+ platform: string;
18
52
  } | {
19
53
  phase: 'done';
20
54
  message: string;
@@ -185,3 +219,160 @@ export interface SetupMcpFailure {
185
219
  error: Error;
186
220
  }
187
221
  export type SetupMcpResult = SetupMcpSuccess | SetupMcpFailure;
222
+ /** Auth option propagated to the Editor as `UNITY_MCP_AUTH_OPTION`. */
223
+ export type OpenProjectAuthOption = 'none' | 'required';
224
+ /** Transport propagated to the Editor as `UNITY_MCP_TRANSPORT`. */
225
+ export type OpenProjectTransport = 'streamableHttp' | 'stdio';
226
+ export interface OpenProjectOptions {
227
+ /**
228
+ * Path to the Unity project to open. Absolute or relative; defaults
229
+ * to `process.cwd()` if omitted.
230
+ */
231
+ projectPath?: string;
232
+ /** Specific Unity Editor version to use (e.g. `"2022.3.62f3"`). */
233
+ unityVersion?: string;
234
+ /**
235
+ * If `true`, skip wiring the MCP connection environment variables
236
+ * onto the spawned editor process. Mirrors the CLI's `--no-connect`
237
+ * flag semantics. Defaults to `false`.
238
+ */
239
+ noConnect?: boolean;
240
+ /** MCP server URL — sets `UNITY_MCP_HOST` on the editor process. */
241
+ url?: string;
242
+ /** Auth token — sets `UNITY_MCP_TOKEN` on the editor process. */
243
+ token?: string;
244
+ /** Auth option — sets `UNITY_MCP_AUTH_OPTION` on the editor process. */
245
+ auth?: OpenProjectAuthOption;
246
+ /** Comma-separated list of tool names — sets `UNITY_MCP_TOOLS`. */
247
+ tools?: string;
248
+ /**
249
+ * If `true`, sets `UNITY_MCP_KEEP_CONNECTED=true`. Auto-enabled by
250
+ * Cloud-mode auto-detection when a `cloudToken` is present in the
251
+ * project's config.
252
+ */
253
+ keepConnected?: boolean;
254
+ /** Transport — sets `UNITY_MCP_TRANSPORT`. */
255
+ transport?: OpenProjectTransport;
256
+ /**
257
+ * If set, sets `UNITY_MCP_START_SERVER=true|false`. Use a boolean to
258
+ * avoid the CLI's stringly-typed `"true"`/`"false"` parse step.
259
+ */
260
+ startServer?: boolean;
261
+ /**
262
+ * If `true` (the default), poll for the Unity Editor's
263
+ * "compile errors at launch" dialog after the editor process has
264
+ * been spawned and click `Ignore` (or the platform-equivalent
265
+ * button) so the editor finishes initialising. Set to `false` to
266
+ * disable the polling loop entirely — corresponds to the CLI's
267
+ * `--no-auto-dismiss-launch-errors` flag.
268
+ *
269
+ * The polling loop runs concurrently with the existing wait-for-
270
+ * ready logic (which is the authoritative ready signal); when no
271
+ * dialog appears, behaviour is unchanged from the pre-feature
272
+ * baseline (no spurious clicks, no extra delay).
273
+ */
274
+ autoDismissLaunchErrors?: boolean;
275
+ /**
276
+ * Overall timeout (milliseconds) for the launch-errors dismissal
277
+ * polling loop. The loop ticks every
278
+ * `launchDismissPollIntervalMs` until either the dialog is
279
+ * dismissed, this timeout elapses, or `openProject` returns. Default
280
+ * `30000` (30 s).
281
+ */
282
+ launchDismissTimeoutMs?: number;
283
+ /**
284
+ * Polling tick interval (milliseconds) for the launch-errors
285
+ * dismissal loop. Default `1500`.
286
+ */
287
+ launchDismissPollIntervalMs?: number;
288
+ /**
289
+ * Optional abort signal that, when fired, stops the launch-errors
290
+ * dismissal polling loop early. Intended for callers that have an
291
+ * authoritative "Unity is ready" signal in scope (e.g. a parallel
292
+ * `wait-for-ready` poll) so the dismissal loop does not keep
293
+ * ticking after Unity has finished initialising.
294
+ *
295
+ * When omitted, the loop falls back to a grace window after the
296
+ * editor process is spawned: if no dialog has been observed within
297
+ * ~15s of polling, the loop exits early on the assumption that the
298
+ * dialog is not going to appear for this launch. The grace window
299
+ * has to cover Unity's full startup phase (process spawn → Package
300
+ * Manager connect → first compile pass) because the launch-errors
301
+ * dialog (`"Enter Safe Mode?"` on Unity 2020.2+) appears at the end
302
+ * of that phase, not the start of it (issue #737).
303
+ */
304
+ launchDismissAbortSignal?: AbortSignal;
305
+ /**
306
+ * Optional progress callback — fires for `start`,
307
+ * `detecting-editor-version`, `editors-located`, `editor-resolved`,
308
+ * `connection-details`, `launching-editor`, `editor-launched`,
309
+ * `launch-errors-dismissed` (only when a dialog was actually
310
+ * dismissed), and `done`.
311
+ */
312
+ onProgress?: ProgressCallback;
313
+ }
314
+ /** Successful `openProject` outcome. Narrow with `kind === 'success'`. */
315
+ export interface OpenProjectSuccess {
316
+ kind: 'success';
317
+ /** Always `true` for the success variant. */
318
+ success: true;
319
+ /** Absolute path to the Unity Editor binary that was launched. */
320
+ editorPath: string;
321
+ /**
322
+ * PID of the spawned editor process. May be `undefined` if the OS
323
+ * has not yet reported a PID by the time the call returns (rare;
324
+ * the value is captured asynchronously from the child process's
325
+ * `spawn` event).
326
+ */
327
+ editorPid?: number;
328
+ /** Editor version string used (e.g. `"2022.3.62f3"`), if known. */
329
+ unityVersion?: string;
330
+ /** Resolved absolute project path. */
331
+ projectPath: string;
332
+ /** Non-fatal warnings collected during the run. */
333
+ warnings: string[];
334
+ /**
335
+ * `true` when an existing Unity Editor process was already running
336
+ * with this project and a launch was therefore skipped. The
337
+ * `editorPid` will be the existing process's PID in that case.
338
+ */
339
+ alreadyRunning?: boolean;
340
+ }
341
+ /** Failed `openProject` outcome. Narrow with `kind === 'failure'`. */
342
+ export interface OpenProjectFailure {
343
+ kind: 'failure';
344
+ /** Always `false` for the failure variant. */
345
+ success: false;
346
+ /** Resolved absolute project path, if it could be determined. */
347
+ projectPath?: string;
348
+ /** Editor path, if locating the editor succeeded. */
349
+ editorPath?: string;
350
+ /** Editor version, if it could be detected before failure. */
351
+ unityVersion?: string;
352
+ /** Non-fatal warnings collected before the failure. */
353
+ warnings: string[];
354
+ /** Human-readable error message — never thrown past the public boundary. */
355
+ errorMessage: string;
356
+ /** The captured error. */
357
+ error: Error;
358
+ }
359
+ export type OpenProjectResult = OpenProjectSuccess | OpenProjectFailure;
360
+ /**
361
+ * Subset of `OpenProjectOptions` consumed by `buildOpenEnv` — every
362
+ * field on this interface is potentially mapped to a `UNITY_MCP_*`
363
+ * environment variable. Declared as a dedicated interface (rather
364
+ * than a `Pick<OpenProjectOptions, …>` re-listed inline) so adding a
365
+ * new env-bearing option to `OpenProjectOptions` is a one-step change
366
+ * here that `buildOpenEnv` picks up by signature, with no risk of the
367
+ * Pick list silently drifting.
368
+ */
369
+ export interface OpenEnvInputs {
370
+ noConnect?: OpenProjectOptions['noConnect'];
371
+ url?: OpenProjectOptions['url'];
372
+ token?: OpenProjectOptions['token'];
373
+ auth?: OpenProjectOptions['auth'];
374
+ tools?: OpenProjectOptions['tools'];
375
+ keepConnected?: OpenProjectOptions['keepConnected'];
376
+ transport?: OpenProjectOptions['transport'];
377
+ startServer?: OpenProjectOptions['startServer'];
378
+ }
package/dist/lib.d.ts CHANGED
@@ -2,4 +2,5 @@ export { installPlugin } from './lib/install-plugin.js';
2
2
  export { removePlugin } from './lib/remove-plugin.js';
3
3
  export { configure } from './lib/configure.js';
4
4
  export { setupMcp, listAgentIds } from './lib/setup-mcp.js';
5
- export type { ProgressEvent, ProgressCallback, ResultKind, InstallPluginOptions, InstallResult, InstallSuccess, InstallFailure, RemovePluginOptions, RemoveResult, RemoveSuccess, RemoveFailure, ConfigureOptions, ConfigureResult, ConfigureSuccess, ConfigureFailure, ConfigureSnapshot, FeatureAction, McpFeatureSnapshot, SetupMcpOptions, SetupMcpResult, SetupMcpSuccess, SetupMcpFailure, McpTransport, } from './lib/types.js';
5
+ export { openProject } from './lib/open.js';
6
+ export type { ProgressEvent, ProgressCallback, ResultKind, InstallPluginOptions, InstallResult, InstallSuccess, InstallFailure, RemovePluginOptions, RemoveResult, RemoveSuccess, RemoveFailure, ConfigureOptions, ConfigureResult, ConfigureSuccess, ConfigureFailure, ConfigureSnapshot, FeatureAction, McpFeatureSnapshot, SetupMcpOptions, SetupMcpResult, SetupMcpSuccess, SetupMcpFailure, McpTransport, OpenProjectOptions, OpenProjectResult, OpenProjectSuccess, OpenProjectFailure, OpenProjectAuthOption, OpenProjectTransport, } from './lib/types.js';
package/dist/lib.js CHANGED
@@ -19,4 +19,5 @@ export { installPlugin } from './lib/install-plugin.js';
19
19
  export { removePlugin } from './lib/remove-plugin.js';
20
20
  export { configure } from './lib/configure.js';
21
21
  export { setupMcp, listAgentIds } from './lib/setup-mcp.js';
22
+ export { openProject } from './lib/open.js';
22
23
  //# sourceMappingURL=lib.js.map
package/dist/lib.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,qDAAqD;AACrD,iEAAiE;AACjE,sEAAsE;AACtE,oDAAoD;AACpD,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,uEAAuE;AACvE,iEAAiE;AACjE,oEAAoE;AACpE,2BAA2B;AAC3B,EAAE;AACF,sEAAsE;AACtE,sDAAsD;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"lib.js","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,qDAAqD;AACrD,iEAAiE;AACjE,sEAAsE;AACtE,oDAAoD;AACpD,yEAAyE;AACzE,0EAA0E;AAC1E,0EAA0E;AAC1E,uEAAuE;AACvE,uEAAuE;AACvE,iEAAiE;AACjE,oEAAoE;AACpE,2BAA2B;AAC3B,EAAE;AACF,sEAAsE;AACtE,sDAAsD;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Supported `process.platform` values for the launch-errors dialog
3
+ * dismiss helper. Narrowed alias of NodeJS.Platform — keeps the
4
+ * platform-dispatch table exhaustive in tests without forcing callers
5
+ * to import a Node-internal type.
6
+ */
7
+ export type DismissPlatform = 'win32' | 'darwin' | 'linux';
8
+ /**
9
+ * Outcome of a single dismiss attempt against the running OS desktop.
10
+ *
11
+ * `dismissed`: the dialog was found AND a click was dispatched
12
+ * successfully. The polling loop should stop and return.
13
+ *
14
+ * `not-found`: no matching dialog was visible on this poll tick. The
15
+ * polling loop should continue ticking until either the overall
16
+ * timeout elapses or Unity reports ready (the existing wait-for-ready
17
+ * logic, which runs in parallel, is the authoritative ready signal).
18
+ *
19
+ * `error`: an unexpected platform error happened (a required tool was
20
+ * missing, a syscall failed). The polling loop logs the error once
21
+ * and continues with `not-found` semantics — the dialog may simply
22
+ * not be open yet, and a single transient error must not abort the
23
+ * whole launch flow.
24
+ */
25
+ export type DismissOutcome = {
26
+ kind: 'dismissed';
27
+ button: string;
28
+ } | {
29
+ kind: 'not-found';
30
+ } | {
31
+ kind: 'error';
32
+ message: string;
33
+ };
34
+ /**
35
+ * Window-title fragments matched against the Unity launch-errors
36
+ * dialog. Both legacy and current strings are listed so the matcher
37
+ * stays resilient across Unity versions. The match is case-insensitive
38
+ * and substring-based — Unity's actual title varies by version but
39
+ * always contains one of these.
40
+ *
41
+ * Exposed so tests can assert the matcher knows about both spellings
42
+ * without grepping the implementation.
43
+ */
44
+ export declare const LAUNCH_ERROR_DIALOG_TITLE_FRAGMENTS: readonly string[];
45
+ /** The button label this helper presses to dismiss the dialog. */
46
+ export declare const DISMISS_BUTTON_LABEL = "Ignore";
47
+ /**
48
+ * Producer-side prefixes for error messages that callers treat as
49
+ * permanent (the polling loop bails out instead of ticking again).
50
+ *
51
+ * Exported so the bailout matcher in `lib/open.ts` and the
52
+ * error-construction sites below reference the SAME literal — a
53
+ * future re-word lands in both places at once.
54
+ *
55
+ * The matcher in `lib/open.ts` uses `String.includes`, so each
56
+ * constant just needs to be a stable substring of the full message.
57
+ */
58
+ export declare const LINUX_XDOTOOL_MISSING_PREFIX = "xdotool not found on PATH";
59
+ export declare const UNSUPPORTED_PLATFORM_PREFIX = "Unsupported platform for launch-errors auto-dismiss";
60
+ /**
61
+ * Try once to find and dismiss the Unity launch-errors dialog on the
62
+ * current OS desktop. Pure-ish — performs a single OS call and
63
+ * returns; never blocks past the underlying syscall's own timeout.
64
+ *
65
+ * Library-safe: never throws (errors are returned in the
66
+ * `DismissOutcome` union), never writes to stdout/stderr, never
67
+ * mutates global state.
68
+ *
69
+ * The helper is platform-dispatched:
70
+ * - **Windows**: Win32 (`FindWindowW` / `EnumWindows` /
71
+ * `EnumChildWindows` / `GetWindowTextW` / `SendMessageW(BM_CLICK)`)
72
+ * driven from PowerShell so we do not pull in a native node-gyp
73
+ * dependency. UI Automation is the documented fallback if title
74
+ * matching breaks on a future Unity release.
75
+ * - **macOS**: AppleScript via `osascript` (the leaner
76
+ * AX-C-API-direct path is a documented follow-up). Requires the
77
+ * user to have granted Accessibility permission to the terminal /
78
+ * `unity-mcp-cli` binary once.
79
+ * - **Linux/X11**: `xdotool` (documented as a Linux platform
80
+ * dependency; `wmctrl` is acceptable as an alternative window
81
+ * enumerator). Wayland is deferred — call out explicitly in the
82
+ * error message so the user does not waste time debugging.
83
+ */
84
+ export declare function tryDismissLaunchErrorsDialog(platform?: DismissPlatform): Promise<DismissOutcome>;
85
+ /**
86
+ * The PowerShell payload that probes for the Unity launch-errors
87
+ * dialog and clicks `Ignore` if found. Exported as a string (not a
88
+ * function) so tests can assert the script shape without launching
89
+ * PowerShell.
90
+ *
91
+ * Strategy:
92
+ * 1. P/Invoke `EnumWindows` via Add-Type to enumerate every visible
93
+ * top-level window owned by `Unity.exe`.
94
+ * 2. Filter by title fragment (case-insensitive substring).
95
+ * 3. Walk child windows with `EnumChildWindows`, looking for a
96
+ * Button whose text equals `Ignore`.
97
+ * 4. Send `BM_CLICK` (0x00F5) to the matched button. `BM_CLICK` is
98
+ * preferred over a synthesised mouse event — it works even if
99
+ * the user is mid-click in another app, and it does not steal
100
+ * focus.
101
+ *
102
+ * The script writes a single-token result to stdout:
103
+ * - `dismissed:<button>` on success
104
+ * - `not-found` when no dialog was matched
105
+ * - `error:<message>` on an unexpected exception
106
+ */
107
+ export declare const WINDOWS_DISMISS_PS_SCRIPT: string;
108
+ /**
109
+ * The AppleScript snippet used for the macOS dismiss path. Exposed as
110
+ * a string for testability.
111
+ *
112
+ * Strategy: iterate every window of the Unity application process and
113
+ * click the first button titled `Ignore`. If the Unity process is not
114
+ * running OR no matching button exists, the script reports
115
+ * `not-found`. Any AppleScript exception (e.g. Accessibility
116
+ * permission not granted) is reported as `error:<message>` so the
117
+ * caller can surface it once and continue polling.
118
+ *
119
+ * Requires the Terminal / `unity-mcp-cli` binary to have been granted
120
+ * Accessibility permission in System Settings → Privacy & Security →
121
+ * Accessibility. Documented in the README.
122
+ */
123
+ export declare const MACOS_DISMISS_APPLESCRIPT = "\non run\n try\n tell application \"System Events\"\n if not (exists process \"Unity\") then\n return \"not-found\"\n end if\n tell process \"Unity\"\n repeat with w in windows\n try\n if exists (button \"Ignore\" of w) then\n click button \"Ignore\" of w\n return \"dismissed:Ignore\"\n end if\n end try\n end repeat\n end tell\n end tell\n return \"not-found\"\n on error errMsg\n return \"error:\" & errMsg\n end try\nend run\n";
124
+ /**
125
+ * Reset the cached `xdotool` presence flag. Test-only — production
126
+ * code never needs to call this. Exposed so tests can simulate "the
127
+ * tool was installed mid-process" without affecting other tests.
128
+ */
129
+ export declare function _resetXdotoolPresenceForTests(): void;
130
+ /**
131
+ * Escape a literal string for safe use in `xdotool search --name`.
132
+ * `xdotool search --name` interprets its argument as a regex; without
133
+ * escaping, a future fragment containing metacharacters (e.g.
134
+ * `(Hold On)` or `Compiler Errors v2.0+`) would silently change the
135
+ * match semantics. The current fragments are regex-safe but this
136
+ * helper is defensive and zero-runtime-cost.
137
+ *
138
+ * Exposed for tests so the regex-safety contract is locked down.
139
+ */
140
+ export declare function regexEscapeForXdotool(s: string): string;
141
+ /**
142
+ * Parse the single-token contract every platform-specific dispatcher
143
+ * writes to stdout. Exposed for tests so we can exhaustively cover
144
+ * the parser without invoking PowerShell / osascript / xdotool.
145
+ *
146
+ * Inspects the LAST non-empty line of stdout, not the whole buffer:
147
+ * a stray PowerShell warning, `osascript` deprecation notice, or
148
+ * `xdotool` chatter printed before the contract token must not
149
+ * misclassify the result as `not-found`.
150
+ *
151
+ * Contract:
152
+ * - `dismissed:<button>` → `{ kind: 'dismissed', button }`
153
+ * - `not-found` → `{ kind: 'not-found' }`
154
+ * - `error:<message>` → `{ kind: 'error', message }`
155
+ * - any other / empty → `{ kind: 'not-found' }` (defensive — a
156
+ * transient parse miss is treated as "not yet visible" rather
157
+ * than aborting the polling loop)
158
+ */
159
+ export declare function parseDismissOutput(stdout: string): DismissOutcome;