unity-mcp-cli 1.2.0 → 1.4.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/CHANGELOG.md +49 -0
- package/README.md +39 -1
- package/dist/commands/close.d.ts +1 -0
- package/dist/commands/close.js +52 -0
- package/dist/commands/close.js.map +1 -1
- package/dist/commands/exec.d.ts +2 -0
- package/dist/commands/exec.js +117 -0
- package/dist/commands/exec.js.map +1 -0
- package/dist/commands/open.js +26 -1
- package/dist/commands/open.js.map +1 -1
- package/dist/commands/run-tool-builder.d.ts +10 -1
- package/dist/commands/run-tool-builder.js +6 -26
- package/dist/commands/run-tool-builder.js.map +1 -1
- package/dist/commands/wait-for-ready.js +18 -62
- package/dist/commands/wait-for-ready.js.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/editor-session.d.ts +73 -0
- package/dist/lib/editor-session.js +187 -0
- package/dist/lib/editor-session.js.map +1 -0
- package/dist/lib/exec.d.ts +82 -0
- package/dist/lib/exec.js +113 -0
- package/dist/lib/exec.js.map +1 -0
- package/dist/lib/open.d.ts +5 -0
- package/dist/lib/open.js +128 -44
- package/dist/lib/open.js.map +1 -1
- package/dist/lib/run-tool.js +1 -1
- package/dist/lib/run-tool.js.map +1 -1
- package/dist/lib/types.d.ts +34 -1
- package/dist/lib/wait-for-ready.d.ts +36 -0
- package/dist/lib/wait-for-ready.js +29 -0
- package/dist/lib/wait-for-ready.js.map +1 -0
- package/dist/lib.d.ts +4 -0
- package/dist/lib.js +2 -0
- package/dist/lib.js.map +1 -1
- package/dist/utils/connection.d.ts +5 -0
- package/dist/utils/connection.js +15 -0
- package/dist/utils/connection.js.map +1 -1
- package/dist/utils/server-version.d.ts +1 -1
- package/dist/utils/server-version.js +1 -1
- package/docs/README.es.md +33 -0
- package/docs/README.ja.md +33 -0
- package/docs/README.zh-CN.md +33 -0
- package/package.json +2 -2
|
@@ -1,29 +1,8 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
2
|
import * as ui from '../utils/ui.js';
|
|
3
3
|
import { verbose } from '../utils/ui.js';
|
|
4
|
-
import { resolveAndValidateProjectPath, resolveConnection } from '../utils/connection.js';
|
|
5
|
-
import {
|
|
6
|
-
import { probe } from '../utils/probe.js';
|
|
7
|
-
const MAX_PROBE_TIMEOUT_MS = 10000;
|
|
8
|
-
/**
|
|
9
|
-
* Build the list of base URLs to probe.
|
|
10
|
-
* When --url is explicit, only that URL is probed.
|
|
11
|
-
* Otherwise, probe both the config-resolved URL and the deterministic local port
|
|
12
|
-
* (if they differ), succeeding on whichever responds first.
|
|
13
|
-
*/
|
|
14
|
-
async function resolveProbeUrls(projectPath, options) {
|
|
15
|
-
const { url: configUrl } = await resolveConnection(projectPath, options);
|
|
16
|
-
const urls = [configUrl];
|
|
17
|
-
if (!options.url) {
|
|
18
|
-
const localPort = generatePortFromDirectory(projectPath);
|
|
19
|
-
const localUrl = `http://localhost:${localPort}`;
|
|
20
|
-
if (localUrl !== configUrl) {
|
|
21
|
-
urls.push(localUrl);
|
|
22
|
-
verbose(`Will probe both config URL (${configUrl}) and local URL (${localUrl})`);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
return urls;
|
|
26
|
-
}
|
|
4
|
+
import { resolveAndValidateProjectPath, resolveConnection, resolveProbeUrls } from '../utils/connection.js';
|
|
5
|
+
import { waitForReady } from '../lib/wait-for-ready.js';
|
|
27
6
|
export const waitForReadyCommand = new Command('wait-for-ready')
|
|
28
7
|
.description('Wait until Unity Editor and MCP server are ready to accept tool calls')
|
|
29
8
|
.argument('[path]', 'Unity project path (used for config and auto port detection)')
|
|
@@ -34,8 +13,8 @@ export const waitForReadyCommand = new Command('wait-for-ready')
|
|
|
34
13
|
.option('--interval <ms>', 'Polling interval in milliseconds (default: 3000)', '3000')
|
|
35
14
|
.action(async (positionalPath, options) => {
|
|
36
15
|
const projectPath = resolveAndValidateProjectPath(positionalPath, options);
|
|
37
|
-
const { token } = await resolveConnection(projectPath, options);
|
|
38
|
-
const probeUrls =
|
|
16
|
+
const { url, token } = await resolveConnection(projectPath, options);
|
|
17
|
+
const probeUrls = resolveProbeUrls(projectPath, url, options.url);
|
|
39
18
|
const timeoutMs = parseInt(options.timeout ?? '120000', 10);
|
|
40
19
|
const intervalMs = parseInt(options.interval ?? '3000', 10);
|
|
41
20
|
if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
|
|
@@ -46,48 +25,25 @@ export const waitForReadyCommand = new Command('wait-for-ready')
|
|
|
46
25
|
ui.error(`Invalid --interval value: "${options.interval}". Must be a positive integer (milliseconds).`);
|
|
47
26
|
process.exit(1);
|
|
48
27
|
}
|
|
49
|
-
const headers = {
|
|
50
|
-
'Content-Type': 'application/json',
|
|
51
|
-
};
|
|
52
|
-
if (token) {
|
|
53
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
54
|
-
}
|
|
55
28
|
for (const url of probeUrls) {
|
|
56
29
|
verbose(`Probe target: ${url}`);
|
|
57
30
|
}
|
|
58
31
|
verbose(`Timeout: ${timeoutMs}ms, Interval: ${intervalMs}ms`);
|
|
59
32
|
const spinner = ui.startSpinner('Waiting for Unity Editor and MCP server...');
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
let pending = probeUrls.length;
|
|
73
|
-
for (const url of probeUrls) {
|
|
74
|
-
probe(url, headers, probeTimeout).then(result => {
|
|
75
|
-
if (result.ok)
|
|
76
|
-
resolve(result);
|
|
77
|
-
else if (--pending === 0)
|
|
78
|
-
resolve(null);
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
if (ready?.ok) {
|
|
83
|
-
const totalSeconds = ((Date.now() - startTime) / 1000).toFixed(1);
|
|
84
|
-
spinner.success(`MCP server is ready at ${ready.baseUrl} (connected in ${totalSeconds}s)`);
|
|
85
|
-
process.exit(0);
|
|
86
|
-
}
|
|
87
|
-
const remainingMs = timeoutMs - (Date.now() - startTime);
|
|
88
|
-
if (remainingMs <= 0)
|
|
89
|
-
continue;
|
|
90
|
-
await new Promise(resolve => setTimeout(resolve, Math.min(intervalMs, remainingMs)));
|
|
33
|
+
const result = await waitForReady({
|
|
34
|
+
urls: probeUrls,
|
|
35
|
+
token,
|
|
36
|
+
timeoutMs,
|
|
37
|
+
intervalMs,
|
|
38
|
+
onTick: remainingMs => {
|
|
39
|
+
spinner.text = `Waiting for Unity Editor and MCP server... (${Math.ceil(remainingMs / 1000)}s remaining)`;
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
if (result.kind === 'success') {
|
|
43
|
+
spinner.success(`MCP server is ready at ${result.baseUrl} (connected in ${(result.elapsedMs / 1000).toFixed(1)}s)`);
|
|
44
|
+
process.exit(0);
|
|
91
45
|
}
|
|
46
|
+
spinner.error(`Timed out after ${(timeoutMs / 1000).toFixed(1)}s waiting for MCP server`);
|
|
47
|
+
process.exit(1);
|
|
92
48
|
});
|
|
93
49
|
//# sourceMappingURL=wait-for-ready.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wait-for-ready.js","sourceRoot":"","sources":["../../src/commands/wait-for-ready.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,OAAO,EAAE,6BAA6B,EAAE,iBAAiB,EAAE,
|
|
1
|
+
{"version":3,"file":"wait-for-ready.js","sourceRoot":"","sources":["../../src/commands/wait-for-ready.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { createRequire } from 'module';
|
|
|
3
3
|
import { bootstrapLocalCommand } from './commands/bootstrap-local.js';
|
|
4
4
|
import { closeCommand } from './commands/close.js';
|
|
5
5
|
import { createProjectCommand } from './commands/create-project.js';
|
|
6
|
+
import { execCommand } from './commands/exec.js';
|
|
6
7
|
import { installUnityCommand } from './commands/install-unity.js';
|
|
7
8
|
import { openCommand } from './commands/open.js';
|
|
8
9
|
import { installExtensionCommand } from './commands/install-extension.js';
|
|
@@ -33,6 +34,7 @@ const subcommands = [
|
|
|
33
34
|
closeCommand,
|
|
34
35
|
configureCommand,
|
|
35
36
|
createProjectCommand,
|
|
37
|
+
execCommand,
|
|
36
38
|
installExtensionCommand,
|
|
37
39
|
installPluginCommand,
|
|
38
40
|
installUnityCommand,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { EditorSession, type SessionProcessOps } from '@baizor/gamedev-cli-core';
|
|
2
|
+
/**
|
|
3
|
+
* Plugin system tool that quits an editor started by a CLI session: it checks the lease nonce, saves like the idle
|
|
4
|
+
* shutdown and exits. Needed because the OS polite quit (`taskkill` without `/F`) never reaches a windowless editor.
|
|
5
|
+
*/
|
|
6
|
+
export declare const SESSION_QUIT_TOOL = "editor-session-quit";
|
|
7
|
+
export interface UnitySessionDeps {
|
|
8
|
+
/** The PID of a Unity Editor process that has the project open; undefined when none. */
|
|
9
|
+
findEditorPid: (projectPath: string) => number | undefined;
|
|
10
|
+
/** Ask the plugin at `url` to quit (`SESSION_QUIT_TOOL`); true when it accepted. */
|
|
11
|
+
quitViaPlugin: (url: string, token: string | undefined, nonce: string) => Promise<boolean>;
|
|
12
|
+
/** The OS polite quit; true when delivered. */
|
|
13
|
+
requestOsClose: (pid: number) => boolean;
|
|
14
|
+
findEditor: (version: string | undefined) => Promise<string | null>;
|
|
15
|
+
/** Drop the cached editor path of `version` (it failed to start), so the next call resolves it again. */
|
|
16
|
+
forgetEditor: (version: string | undefined) => void;
|
|
17
|
+
processOps?: Partial<SessionProcessOps>;
|
|
18
|
+
fetchImpl?: typeof fetch;
|
|
19
|
+
}
|
|
20
|
+
export interface UnitySessionOptions {
|
|
21
|
+
projectPath: string;
|
|
22
|
+
/**
|
|
23
|
+
* The project's server URLs. A loopback one answering means the editor is up; a remote (cloud) one answers whether
|
|
24
|
+
* or not this editor runs, so it counts only while a Unity process has the project open.
|
|
25
|
+
*/
|
|
26
|
+
urls: string[];
|
|
27
|
+
token?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The server URL a started editor connects to; recorded in the session lease, so `close` reaches the editor's plugin
|
|
30
|
+
* there without resolving the connection again.
|
|
31
|
+
*/
|
|
32
|
+
serverUrl?: string;
|
|
33
|
+
/** Extra environment for a started editor (`UNITY_MCP_*` connection settings). */
|
|
34
|
+
env?: Record<string, string>;
|
|
35
|
+
/** Explicit editor executable; default: the installed editor of the project's (or `unityVersion`) version. */
|
|
36
|
+
editorPath?: string;
|
|
37
|
+
unityVersion?: string;
|
|
38
|
+
/** A started editor quits after this many seconds without tool calls; 0 = never. Default 300. */
|
|
39
|
+
idleSeconds?: number;
|
|
40
|
+
readyTimeoutMs?: number;
|
|
41
|
+
/** Wait for an editor the CLI did not start. Default `DEFAULT_FOREIGN_READY_TIMEOUT_MS` (at most `readyTimeoutMs`). */
|
|
42
|
+
foreignReadyTimeoutMs?: number;
|
|
43
|
+
onStatus?: (message: string) => void;
|
|
44
|
+
/** Called once this session has started an editor process (not when it reuses one). */
|
|
45
|
+
onLaunched?: (pid: number) => void;
|
|
46
|
+
deps?: Partial<UnitySessionDeps>;
|
|
47
|
+
}
|
|
48
|
+
export declare function isLoopbackUrl(url: string): boolean;
|
|
49
|
+
/** The project's Unity editor session. The readiness value is the server URL that answered. */
|
|
50
|
+
export declare function createUnitySession(options: UnitySessionOptions): EditorSession<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Whether the project has a session lease whose editor process may still run. Cheap (no process start-time check): a
|
|
53
|
+
* dead lease, which an editor leaves behind when it exits, does not count; the session removes it.
|
|
54
|
+
*/
|
|
55
|
+
export declare function hasLiveSessionLease(projectPath: string, isAlive?: (pid: number) => boolean): boolean;
|
|
56
|
+
/**
|
|
57
|
+
* The message for a session failure. cli-core already explains an editor the CLI did not start that never answered;
|
|
58
|
+
* in Cloud mode (`serverUrl` is the cloud) the CLI reaches that editor only through ai-game.dev, which gets a hint.
|
|
59
|
+
*/
|
|
60
|
+
export declare function sessionErrorMessage(err: Error, serverUrl: string | undefined): string;
|
|
61
|
+
/**
|
|
62
|
+
* The project's editor session, reaching its server the way the other commands resolve it (explicit url / token,
|
|
63
|
+
* then the project config, then the project's local port).
|
|
64
|
+
*/
|
|
65
|
+
export declare function createProjectSession(projectPath: string, connection: {
|
|
66
|
+
url?: string;
|
|
67
|
+
token?: string;
|
|
68
|
+
}, extra?: Partial<UnitySessionOptions>, factory?: (options: UnitySessionOptions) => EditorSession<string>): Promise<EditorSession<string>>;
|
|
69
|
+
/**
|
|
70
|
+
* An explicit editor path, resolved and checked to be a file. Checked before `path.resolve`: an empty string would
|
|
71
|
+
* resolve to the working directory.
|
|
72
|
+
*/
|
|
73
|
+
export declare function validateEditorPath(editorPath: string): string;
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// Unity editor sessions on cli-core's `EditorSession` (Code-Exec plan §4.6), shared by `exec`, `open` and `close`.
|
|
2
|
+
// A lease (`<project>/.ai-game-dev/session.json`) marks an editor this CLI started, a file lock lets only one call
|
|
3
|
+
// start an editor per project, and only a leased editor is ever closed. Library-safe: no console output, no exit.
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as os from 'os';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { EditorSession, EditorSessionError, DEFAULT_SESSION_READY_TIMEOUT_MS, defaultSessionProcessOps, readSessionLease, isLoopbackHost, mcpReadinessProbe, unityAdapter, } from '@baizor/gamedev-cli-core';
|
|
8
|
+
import { findEditorPath, getProjectEditorVersion } from '../utils/unity-editor.js';
|
|
9
|
+
import { clearCachedEditorPath } from '../utils/editor-cache.js';
|
|
10
|
+
import { resolveConnection, resolveProbeUrls } from '../utils/connection.js';
|
|
11
|
+
import { CLOUD_SERVER_URL } from '../utils/config.js';
|
|
12
|
+
import { findUnityProcess } from '../utils/unity-process.js';
|
|
13
|
+
import { sendGracefulShutdown } from '../utils/unity-shutdown.js';
|
|
14
|
+
import { runSystemTool } from './run-tool.js';
|
|
15
|
+
import { readToolResponse } from './exec.js';
|
|
16
|
+
/**
|
|
17
|
+
* Plugin system tool that quits an editor started by a CLI session: it checks the lease nonce, saves like the idle
|
|
18
|
+
* shutdown and exits. Needed because the OS polite quit (`taskkill` without `/F`) never reaches a windowless editor.
|
|
19
|
+
*/
|
|
20
|
+
export const SESSION_QUIT_TOOL = 'editor-session-quit';
|
|
21
|
+
const QUIT_CALL_TIMEOUT_MS = 15000;
|
|
22
|
+
/**
|
|
23
|
+
* Default wait for an editor the CLI did not start (the user's GUI) to answer. Shorter than the start timeout: such an
|
|
24
|
+
* editor may never answer here (plugin disconnected, or connected to the cloud), so the call fails with a clear hint.
|
|
25
|
+
*/
|
|
26
|
+
const DEFAULT_FOREIGN_READY_TIMEOUT_MS = 60000;
|
|
27
|
+
const defaultDeps = {
|
|
28
|
+
findEditorPid: projectPath => findUnityProcess(projectPath)?.pid,
|
|
29
|
+
quitViaPlugin: async (url, token, nonce) => {
|
|
30
|
+
const result = await runSystemTool({
|
|
31
|
+
toolName: SESSION_QUIT_TOOL,
|
|
32
|
+
url,
|
|
33
|
+
...(token ? { token } : {}),
|
|
34
|
+
input: { nonce },
|
|
35
|
+
timeoutMs: QUIT_CALL_TIMEOUT_MS,
|
|
36
|
+
});
|
|
37
|
+
return result.kind === 'success' && readToolResponse(result.data).ok;
|
|
38
|
+
},
|
|
39
|
+
requestOsClose: pid => sendGracefulShutdown(pid),
|
|
40
|
+
findEditor: version => findEditorPath(version),
|
|
41
|
+
forgetEditor: version => clearCachedEditorPath(version),
|
|
42
|
+
};
|
|
43
|
+
export function isLoopbackUrl(url) {
|
|
44
|
+
try {
|
|
45
|
+
const host = new URL(url).hostname;
|
|
46
|
+
return host === 'localhost' || isLoopbackHost(host);
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** The project's Unity editor session. The readiness value is the server URL that answered. */
|
|
53
|
+
export function createUnitySession(options) {
|
|
54
|
+
const deps = { ...defaultDeps, ...options.deps };
|
|
55
|
+
const projectPath = path.resolve(options.projectPath);
|
|
56
|
+
const probe = (urls) => mcpReadinessProbe({
|
|
57
|
+
urls, token: options.token, ...(deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}),
|
|
58
|
+
});
|
|
59
|
+
const probeLocal = probe(options.urls.filter(isLoopbackUrl));
|
|
60
|
+
const remoteUrls = options.urls.filter(url => !isLoopbackUrl(url));
|
|
61
|
+
const probeRemote = probe(remoteUrls);
|
|
62
|
+
// The process scan is slow (PowerShell on Windows): once an editor was seen, polling does not rescan.
|
|
63
|
+
let editorSeen = false;
|
|
64
|
+
const editorRunning = () => (editorSeen || (editorSeen = deps.findEditorPid(projectPath) !== undefined));
|
|
65
|
+
const probeReady = async () => (await probeLocal()) ?? (remoteUrls.length > 0 && editorRunning() ? probeRemote() : undefined);
|
|
66
|
+
const editor = {
|
|
67
|
+
...unityAdapter.editor,
|
|
68
|
+
// The PID lets the session notice cheaply when the user's editor exits while it waits for it.
|
|
69
|
+
findRunningEditor: async (root) => {
|
|
70
|
+
const pid = deps.findEditorPid(root);
|
|
71
|
+
editorSeen || (editorSeen = pid !== undefined); // spares probeReady a second scan while the session waits for it
|
|
72
|
+
return pid ?? false;
|
|
73
|
+
},
|
|
74
|
+
// `lease` is the verified lease naming `pid` (cli-core never passes a dead one): the plugin is asked at the server it
|
|
75
|
+
// was started against, then at whichever of this call's URLs answers.
|
|
76
|
+
requestClose: async (pid, _root, lease) => {
|
|
77
|
+
if (lease) {
|
|
78
|
+
const asked = new Set();
|
|
79
|
+
const quit = async (url) => {
|
|
80
|
+
if (url === undefined || asked.has(url))
|
|
81
|
+
return false;
|
|
82
|
+
asked.add(url);
|
|
83
|
+
// The token was resolved for this call's URLs: never hand it to another remote server.
|
|
84
|
+
const token = options.urls.includes(url) || isLoopbackUrl(url) ? options.token : undefined;
|
|
85
|
+
return deps.quitViaPlugin(url, token, lease.nonce);
|
|
86
|
+
};
|
|
87
|
+
if (await quit(lease.serverUrl) || await quit(await probeReady()))
|
|
88
|
+
return true;
|
|
89
|
+
}
|
|
90
|
+
return deps.requestOsClose(pid);
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
// Set when the binary came from discovery (and so maybe from the editor-path cache), not from `editorPath`.
|
|
94
|
+
let discovered;
|
|
95
|
+
const spawnDetached = deps.processOps?.spawnDetached ?? defaultSessionProcessOps.spawnDetached;
|
|
96
|
+
const processOps = {
|
|
97
|
+
...deps.processOps,
|
|
98
|
+
// A discovered binary that fails to start (corrupt, wrong arch, permissions) leaves the cache, as `open` does.
|
|
99
|
+
spawnDetached: async (command, args, spawnOptions) => {
|
|
100
|
+
try {
|
|
101
|
+
const pid = await spawnDetached(command, args, spawnOptions);
|
|
102
|
+
options.onLaunched?.(pid);
|
|
103
|
+
return pid;
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
if (discovered)
|
|
107
|
+
deps.forgetEditor(discovered.version);
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_SESSION_READY_TIMEOUT_MS;
|
|
113
|
+
return new EditorSession({
|
|
114
|
+
adapter: { ...unityAdapter, editor },
|
|
115
|
+
projectRoot: projectPath,
|
|
116
|
+
probeReady,
|
|
117
|
+
editorBinary: async () => {
|
|
118
|
+
if (options.editorPath !== undefined)
|
|
119
|
+
return validateEditorPath(options.editorPath);
|
|
120
|
+
const version = options.unityVersion ?? getProjectEditorVersion(projectPath) ?? undefined;
|
|
121
|
+
const found = await deps.findEditor(version);
|
|
122
|
+
if (!found)
|
|
123
|
+
throw new Error(version ? `Unity Editor ${version} is not installed.` : 'No Unity Editor found.');
|
|
124
|
+
discovered = { version };
|
|
125
|
+
return found;
|
|
126
|
+
},
|
|
127
|
+
...(options.env ? { env: options.env } : {}),
|
|
128
|
+
...(options.serverUrl !== undefined
|
|
129
|
+
? { serverUrl: options.serverUrl, connectionMode: options.serverUrl === CLOUD_SERVER_URL ? 'Cloud' : 'Custom' }
|
|
130
|
+
: {}),
|
|
131
|
+
...(options.idleSeconds !== undefined ? { idleSeconds: options.idleSeconds } : {}),
|
|
132
|
+
readyTimeoutMs,
|
|
133
|
+
foreignReadyTimeoutMs: options.foreignReadyTimeoutMs ?? Math.min(DEFAULT_FOREIGN_READY_TIMEOUT_MS, readyTimeoutMs),
|
|
134
|
+
...(options.onStatus ? { onStatus: options.onStatus } : {}),
|
|
135
|
+
processOps,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Whether the project has a session lease whose editor process may still run. Cheap (no process start-time check): a
|
|
140
|
+
* dead lease, which an editor leaves behind when it exits, does not count; the session removes it.
|
|
141
|
+
*/
|
|
142
|
+
export function hasLiveSessionLease(projectPath, isAlive = defaultSessionProcessOps.isAlive) {
|
|
143
|
+
const lease = readSessionLease(projectPath);
|
|
144
|
+
// A lease of another machine (a shared folder) cannot be checked here: count it.
|
|
145
|
+
return lease !== undefined && (lease.hostId !== os.hostname() || isAlive(lease.pid));
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The message for a session failure. cli-core already explains an editor the CLI did not start that never answered;
|
|
149
|
+
* in Cloud mode (`serverUrl` is the cloud) the CLI reaches that editor only through ai-game.dev, which gets a hint.
|
|
150
|
+
*/
|
|
151
|
+
export function sessionErrorMessage(err, serverUrl) {
|
|
152
|
+
const { message } = err;
|
|
153
|
+
const foreignNotReady = err instanceof EditorSessionError && err.source === 'foreign' && err.reason === 'not-ready';
|
|
154
|
+
if (!foreignNotReady || serverUrl !== CLOUD_SERVER_URL)
|
|
155
|
+
return message;
|
|
156
|
+
return `${message}. The project is in Cloud mode: the CLI reaches this editor only while its plugin is connected to ` +
|
|
157
|
+
'ai-game.dev. Connect it, switch its connection to Custom (a local server), or pass --url <server url>.';
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* The project's editor session, reaching its server the way the other commands resolve it (explicit url / token,
|
|
161
|
+
* then the project config, then the project's local port).
|
|
162
|
+
*/
|
|
163
|
+
export async function createProjectSession(projectPath, connection, extra = {}, factory = createUnitySession) {
|
|
164
|
+
const { url, token } = await resolveConnection(projectPath, connection);
|
|
165
|
+
return factory({ projectPath, urls: resolveProbeUrls(projectPath, url, connection.url), token, serverUrl: url, ...extra });
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* An explicit editor path, resolved and checked to be a file. Checked before `path.resolve`: an empty string would
|
|
169
|
+
* resolve to the working directory.
|
|
170
|
+
*/
|
|
171
|
+
export function validateEditorPath(editorPath) {
|
|
172
|
+
const trimmed = typeof editorPath === 'string' ? editorPath.trim() : '';
|
|
173
|
+
if (trimmed.length === 0)
|
|
174
|
+
throw new Error('Explicit Unity Editor path is empty or whitespace-only.');
|
|
175
|
+
const resolved = path.resolve(trimmed);
|
|
176
|
+
let stat;
|
|
177
|
+
try {
|
|
178
|
+
stat = fs.statSync(resolved);
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
throw new Error(`Unity Editor path does not exist: ${resolved}`);
|
|
182
|
+
}
|
|
183
|
+
if (!stat.isFile())
|
|
184
|
+
throw new Error(`Unity Editor path is not a file: ${resolved}`);
|
|
185
|
+
return resolved;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=editor-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"editor-session.js","sourceRoot":"","sources":["../../src/lib/editor-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { type EditorSession, type EditorSessionErrorReason, type WaitedEditorSource } from '@baizor/gamedev-cli-core';
|
|
2
|
+
import { type UnitySessionOptions } from './editor-session.js';
|
|
3
|
+
import type { RunToolFailure, RunToolOptions, RunToolResult } from './types.js';
|
|
4
|
+
export declare const SCRIPT_EXECUTE_TOOL = "script-execute";
|
|
5
|
+
/**
|
|
6
|
+
* Default `exec` call timeout: the MCP server's default `tool-timeout` (300 s, McpPlugin 8.11.0+, which the Unity plugin
|
|
7
|
+
* launches its server with) plus 10 s, so the CLI never gives up before the server answers with its own timeout error.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_EXEC_TIMEOUT_MS = 310000;
|
|
10
|
+
/** Injection points for tests. */
|
|
11
|
+
export interface ExecDeps {
|
|
12
|
+
session: (options: UnitySessionOptions) => Pick<EditorSession<string>, 'ensure'>;
|
|
13
|
+
invoke: (options: RunToolOptions) => Promise<RunToolResult>;
|
|
14
|
+
}
|
|
15
|
+
export interface ExecCodeOptions {
|
|
16
|
+
projectPath: string;
|
|
17
|
+
code: string;
|
|
18
|
+
usings?: string[];
|
|
19
|
+
include?: string[];
|
|
20
|
+
/** Start the editor with a window instead of headless (only when it is not running yet). */
|
|
21
|
+
window?: boolean;
|
|
22
|
+
/** An editor started here quits after this many seconds without tool calls (0 = never). Default 300. */
|
|
23
|
+
idleTimeoutSeconds?: number;
|
|
24
|
+
/** Default 300000. */
|
|
25
|
+
readyTimeoutMs?: number;
|
|
26
|
+
/** Wait for an editor exec did not start (the user's GUI) to answer; see `UnitySessionOptions.foreignReadyTimeoutMs`. */
|
|
27
|
+
foreignReadyTimeoutMs?: number;
|
|
28
|
+
/** Tool call timeout. Default `DEFAULT_EXEC_TIMEOUT_MS` (310000). */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
/** Server URLs to try, first answer wins (the CLI passes the config URL plus the project's local port). */
|
|
31
|
+
probeUrls: string[];
|
|
32
|
+
/** Bearer for the server. */
|
|
33
|
+
token?: string;
|
|
34
|
+
/** `token` came from the shared machine store: a 401 refreshes it once. */
|
|
35
|
+
tokenFromCloudStore?: boolean;
|
|
36
|
+
/** Explicit server URL / token: handed to a newly started editor so its plugin connects there. */
|
|
37
|
+
overrideUrl?: string;
|
|
38
|
+
overrideToken?: string;
|
|
39
|
+
onStatus?: (message: string) => void;
|
|
40
|
+
deps?: Partial<ExecDeps>;
|
|
41
|
+
}
|
|
42
|
+
export interface ExecCodeSuccess {
|
|
43
|
+
kind: 'success';
|
|
44
|
+
success: true;
|
|
45
|
+
/** The server that ran the code. */
|
|
46
|
+
baseUrl: string;
|
|
47
|
+
/** Parsed `script-execute` response body. */
|
|
48
|
+
data: unknown;
|
|
49
|
+
}
|
|
50
|
+
export interface ExecCodeFailure {
|
|
51
|
+
kind: 'failure';
|
|
52
|
+
success: false;
|
|
53
|
+
/** `launch-failed`: no editor could be started; `not-ready`: its server never answered; `tool-failed`: the call failed. */
|
|
54
|
+
reason: 'launch-failed' | 'not-ready' | 'tool-failed';
|
|
55
|
+
/** The editor-session failure behind `launch-failed` / `not-ready`. */
|
|
56
|
+
sessionReason?: EditorSessionErrorReason;
|
|
57
|
+
/** `not-ready`: the editor that never answered — `foreign` is one exec did not start (e.g. the user's GUI). */
|
|
58
|
+
editorSource?: WaitedEditorSource;
|
|
59
|
+
/** The server that was called (`tool-failed` only). */
|
|
60
|
+
baseUrl?: string;
|
|
61
|
+
/** The underlying call failure (`tool-failed` only). */
|
|
62
|
+
toolFailure?: RunToolFailure;
|
|
63
|
+
message: string;
|
|
64
|
+
error?: Error;
|
|
65
|
+
}
|
|
66
|
+
export type ExecCodeResult = ExecCodeSuccess | ExecCodeFailure;
|
|
67
|
+
/**
|
|
68
|
+
* Run C# in the project's editor. A running editor (GUI or headless) is reused and never closed; otherwise one is
|
|
69
|
+
* started headless (or windowed) under the project's session lease and start lock, so parallel calls share one
|
|
70
|
+
* editor. It quits by itself after `idleTimeoutSeconds` without tool calls, so the next call stays warm.
|
|
71
|
+
*/
|
|
72
|
+
export declare function execCode(options: ExecCodeOptions): Promise<ExecCodeResult>;
|
|
73
|
+
/** Request body for `script-execute`; empty lists are left out so older plugins without them still accept it. */
|
|
74
|
+
export declare function buildScriptExecuteInput(code: string, usings: string[], include: string[]): Record<string, unknown>;
|
|
75
|
+
/**
|
|
76
|
+
* The tool's text output and whether it reported success. A tool that returns a value (as `script-execute` does) is
|
|
77
|
+
* answered as `{ status, structured: { result } }`; the result alone is the output then.
|
|
78
|
+
*/
|
|
79
|
+
export declare function readToolResponse(data: unknown): {
|
|
80
|
+
ok: boolean;
|
|
81
|
+
text: string;
|
|
82
|
+
};
|
package/dist/lib/exec.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
// Library-safe `execCode`: run C# in a project's Unity Editor through `script-execute`, starting a warm
|
|
2
|
+
// headless editor when none is running. No console output, no `process.exit`.
|
|
3
|
+
import { DEFAULT_SESSION_IDLE_SECONDS, EditorSessionError, } from '@baizor/gamedev-cli-core';
|
|
4
|
+
import { createUnitySession, isLoopbackUrl, sessionErrorMessage } from './editor-session.js';
|
|
5
|
+
import { buildOpenEnv, dismissLaunchErrorsUntil } from './open.js';
|
|
6
|
+
import { runTool } from './run-tool.js';
|
|
7
|
+
export const SCRIPT_EXECUTE_TOOL = 'script-execute';
|
|
8
|
+
/**
|
|
9
|
+
* Default `exec` call timeout: the MCP server's default `tool-timeout` (300 s, McpPlugin 8.11.0+, which the Unity plugin
|
|
10
|
+
* launches its server with) plus 10 s, so the CLI never gives up before the server answers with its own timeout error.
|
|
11
|
+
*/
|
|
12
|
+
export const DEFAULT_EXEC_TIMEOUT_MS = 310000;
|
|
13
|
+
/**
|
|
14
|
+
* Run C# in the project's editor. A running editor (GUI or headless) is reused and never closed; otherwise one is
|
|
15
|
+
* started headless (or windowed) under the project's session lease and start lock, so parallel calls share one
|
|
16
|
+
* editor. It quits by itself after `idleTimeoutSeconds` without tool calls, so the next call stays warm.
|
|
17
|
+
*/
|
|
18
|
+
export async function execCode(options) {
|
|
19
|
+
const deps = { session: createUnitySession, invoke: runTool, ...options.deps };
|
|
20
|
+
// The server a started editor connects to (recorded in its lease): the explicit URL, normalized as the CLI resolves
|
|
21
|
+
// it (no trailing slash), else the resolved one.
|
|
22
|
+
const serverUrl = options.overrideUrl?.replace(/\/$/, '') ?? options.probeUrls[0];
|
|
23
|
+
let baseUrl;
|
|
24
|
+
// A windowed editor we start may stop on the "compile errors at launch" dialog; click it away until it is ready.
|
|
25
|
+
const ready = new AbortController();
|
|
26
|
+
let dismissing;
|
|
27
|
+
try {
|
|
28
|
+
const session = deps.session({
|
|
29
|
+
projectPath: options.projectPath,
|
|
30
|
+
urls: options.probeUrls,
|
|
31
|
+
token: options.token,
|
|
32
|
+
...(serverUrl !== undefined ? { serverUrl } : {}),
|
|
33
|
+
env: buildOpenEnv({
|
|
34
|
+
keepConnected: true,
|
|
35
|
+
url: options.overrideUrl,
|
|
36
|
+
token: options.overrideToken,
|
|
37
|
+
// A local server is only reachable if someone starts it; the plugin does when asked.
|
|
38
|
+
...(options.overrideUrl && isLoopbackUrl(options.overrideUrl) ? { startServer: true } : {}),
|
|
39
|
+
}),
|
|
40
|
+
idleSeconds: options.idleTimeoutSeconds ?? DEFAULT_SESSION_IDLE_SECONDS,
|
|
41
|
+
...(options.readyTimeoutMs !== undefined ? { readyTimeoutMs: options.readyTimeoutMs } : {}),
|
|
42
|
+
...(options.foreignReadyTimeoutMs !== undefined ? { foreignReadyTimeoutMs: options.foreignReadyTimeoutMs } : {}),
|
|
43
|
+
...(options.onStatus ? { onStatus: options.onStatus } : {}),
|
|
44
|
+
...(options.window ? { onLaunched: () => { dismissing = dismissLaunchErrorsUntil(ready.signal, []); } } : {}),
|
|
45
|
+
});
|
|
46
|
+
baseUrl = (await session.ensure({ mode: options.window ? 'window' : 'headless' })).ready;
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
50
|
+
const sessionError = err instanceof EditorSessionError ? err : undefined;
|
|
51
|
+
const sessionReason = sessionError?.reason;
|
|
52
|
+
const reason = sessionReason === 'exited' || sessionReason === 'not-ready' ? 'not-ready' : 'launch-failed';
|
|
53
|
+
return {
|
|
54
|
+
kind: 'failure',
|
|
55
|
+
success: false,
|
|
56
|
+
reason,
|
|
57
|
+
...(sessionReason ? { sessionReason } : {}),
|
|
58
|
+
...(sessionError?.source ? { editorSource: sessionError.source } : {}),
|
|
59
|
+
message: sessionErrorMessage(error, serverUrl),
|
|
60
|
+
error,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
ready.abort();
|
|
65
|
+
await dismissing;
|
|
66
|
+
}
|
|
67
|
+
options.onStatus?.('Running...');
|
|
68
|
+
const result = await deps.invoke({
|
|
69
|
+
toolName: SCRIPT_EXECUTE_TOOL,
|
|
70
|
+
url: baseUrl,
|
|
71
|
+
...(options.token ? { token: options.token } : {}),
|
|
72
|
+
tokenFromCloudStore: options.tokenFromCloudStore,
|
|
73
|
+
input: buildScriptExecuteInput(options.code, options.usings ?? [], options.include ?? []),
|
|
74
|
+
timeoutMs: options.timeoutMs ?? DEFAULT_EXEC_TIMEOUT_MS,
|
|
75
|
+
});
|
|
76
|
+
if (result.kind === 'failure') {
|
|
77
|
+
return {
|
|
78
|
+
kind: 'failure', success: false, reason: 'tool-failed', baseUrl, toolFailure: result, message: result.message, error: result.error,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
return { kind: 'success', success: true, baseUrl, data: result.data };
|
|
82
|
+
}
|
|
83
|
+
/** Request body for `script-execute`; empty lists are left out so older plugins without them still accept it. */
|
|
84
|
+
export function buildScriptExecuteInput(code, usings, include) {
|
|
85
|
+
return {
|
|
86
|
+
csharpCode: code,
|
|
87
|
+
...(usings.length > 0 ? { usings } : {}),
|
|
88
|
+
...(include.length > 0 ? { include } : {}),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The tool's text output and whether it reported success. A tool that returns a value (as `script-execute` does) is
|
|
93
|
+
* answered as `{ status, structured: { result } }`; the result alone is the output then.
|
|
94
|
+
*/
|
|
95
|
+
export function readToolResponse(data) {
|
|
96
|
+
if (typeof data === 'string')
|
|
97
|
+
return { ok: true, text: data };
|
|
98
|
+
const response = data;
|
|
99
|
+
const ok = response?.status === undefined || response.status === 'success';
|
|
100
|
+
const structured = response?.structured;
|
|
101
|
+
if (structured !== undefined && structured !== null) {
|
|
102
|
+
const value = typeof structured === 'object' && 'result' in structured
|
|
103
|
+
? structured.result
|
|
104
|
+
: structured;
|
|
105
|
+
return { ok, text: typeof value === 'string' ? value : JSON.stringify(value) ?? '' };
|
|
106
|
+
}
|
|
107
|
+
const texts = (response?.content ?? []).filter(c => c?.type === 'text' && typeof c.text === 'string').map(c => c.text);
|
|
108
|
+
return {
|
|
109
|
+
ok,
|
|
110
|
+
text: texts.length > 0 ? texts.join('\n') : data === undefined ? '' : JSON.stringify(data),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=exec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.js","sourceRoot":"","sources":["../../src/lib/exec.ts"],"names":[],"mappings":"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"}
|